Skip to content

Commit

Permalink
feat: set fly canister address after creation
Browse files Browse the repository at this point in the history
  • Loading branch information
veeso committed Jan 21, 2024
1 parent 58e349b commit 1eb4293
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const ChangeOwnerForm = () => {
});
};

const btnDisabled = newOwner.length == 0 || pendingTx;
const btnDisabled = newOwner.length != 42 || pendingTx;

return (
<Container.FlexCols className="items-center">
Expand Down
4 changes: 4 additions & 0 deletions ethereum/fly/app/src/js/components/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import TransferForm from './Transfer/TransferForm';
import RenounceOwnershipForm from './RenounceOwnership/RenounceOwnershipForm';
import SwappedSupply from './SwappedSupply';
import MintTestnetTokens from './MintTestnetTokens/MintTestnetTokens';
import SetFlyCanisterAddressForm from './SetFlyCanisterAddress/SetFlyCanisterAddressForm';

const Form = () => {
const { status } = useMetaMask();
Expand All @@ -31,6 +32,9 @@ const Form = () => {
<Card>
<RenounceOwnershipForm />
</Card>
<Card>
<SetFlyCanisterAddressForm />
</Card>
</Container.FlexCols>
) : (
<Container.Container>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react';
import { useConnectedMetaMask } from 'metamask-react';

import Container from '../../reusable/Container';
import Heading from '../../reusable/Heading';
import Input from '../../reusable/Input';
import Button from '../../reusable/Button';
import Web3Client from '../../../web3/Web3Client';
import Alerts from '../../reusable/Alerts';
import { ChainId } from '../../MetamaskConnect';

const setFlyCanisterAddressForm = () => {
const { account, ethereum, chainId } = useConnectedMetaMask();
const [address, setAddress] = React.useState('');
const [addressSet, setAddressSet] = React.useState<string | null>(null);
const [pendingTx, setPendingTx] = React.useState<boolean>(false);
const [error, setError] = React.useState<string>();

const onAddressChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setAddress(event.target.value);
};

const onChangeAddress = () => {
setPendingTx(true);
const client = new Web3Client(account, ethereum, chainId as ChainId);
client
.setFlyCanisterAddress(address)
.then(() => {
setPendingTx(false);
setError(undefined);
})
.catch((e) => {
setError(e.message);
setPendingTx(false);
});
};

React.useEffect(() => {
if (account && ethereum && chainId) {
const client = new Web3Client(account, ethereum, chainId as ChainId);
client
.getFlyCanisterAddress()
.then((address) => {
setAddressSet(address);
})
.catch((e) => {
console.log(e);
setAddressSet(null);
});
}
}, [account, ethereum, chainId]);

const btnDisabled = address.length != 42 || pendingTx;

return (
<Container.FlexCols className="items-center">
{addressSet ? (
<Heading.H2>Fly canister address: {addressSet}</Heading.H2>
) : (
<>
<Heading.H2>Set Fly canister address</Heading.H2>
<Input.Input
id="change-fly-canister-form-address"
label="Fly canister ETH address"
onChange={onAddressChange}
value={address}
/>
<Button.Danger
disabled={btnDisabled}
onClick={onChangeAddress}
className="!mt-4"
>
Set fly canister address
</Button.Danger>
{error && (
<Alerts.Danger>
<p>{error}</p>
</Alerts.Danger>
)}
</>
)}
</Container.FlexCols>
);
};

export default setFlyCanisterAddressForm;
12 changes: 12 additions & 0 deletions ethereum/fly/app/src/js/web3/Web3Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ export default class Web3Client {
.send({ from: this.address });
}

async setFlyCanisterAddress(newAddress: string) {
const contract = this.getContract();
return contract.methods
.setFlyCanisterAddress(newAddress)
.send({ from: this.address });
}

async getFlyCanisterAddress(): Promise<string> {
const contract = this.getContract();
return contract.methods.getFlyCanisterAddress().call();
}

async renounceOwnership() {
const contract = this.getContract();
return contract.methods.renounceOwnership().send({ from: this.address });
Expand Down
18 changes: 13 additions & 5 deletions ethereum/fly/app/src/js/web3/contracts/Fly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ export const ABI = [
name: '_initialOwner',
type: 'address',
},
{
internalType: 'address',
name: '_fly_canister_address',
type: 'address',
},
{
internalType: 'uint256',
name: '_swapFee',
Expand Down Expand Up @@ -368,6 +363,19 @@ export const ABI = [
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: '_fly_canister_address',
type: 'address',
},
],
name: 'setFlyCanisterAddress',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
Expand Down
31 changes: 27 additions & 4 deletions ethereum/fly/contracts/Fly.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
// import "hardhat/console.sol";

contract Fly is ERC20, Ownable {
address private immutable fly_canister_address;
address private fly_canister_address;
uint8 private _decimals;
uint256 public swapFee;

Expand All @@ -23,17 +23,17 @@ contract Fly is ERC20, Ownable {

constructor(
address _initialOwner,
address _fly_canister_address,
uint256 _swapFee
) ERC20("Fly", "FLY") Ownable(_initialOwner) {
_decimals = 12;
fly_canister_address = _fly_canister_address;
swapFee = _swapFee;
fly_canister_address = address(0);
}

modifier onlyFlyCanister() {
require(
msg.sender == fly_canister_address,
msg.sender == fly_canister_address &&
fly_canister_address != address(0),
"Fly: caller is not the fly canister"
);
_;
Expand Down Expand Up @@ -69,9 +69,27 @@ contract Fly is ERC20, Ownable {
* @return The address of the fly canister.
*/
function getFlyCanisterAddress() public view returns (address) {
require(
fly_canister_address != address(0),
"Fly: fly canister address not set"
);
return fly_canister_address;
}

/**
* @dev Sets the address of the fly canister. The address can only be set once.
* @param _fly_canister_address The new address of the fly canister.
*/
function setFlyCanisterAddress(
address _fly_canister_address
) public onlyOwner {
require(
fly_canister_address == address(0),
"Fly: fly canister address already set"
);
fly_canister_address = _fly_canister_address;
}

/**
* @dev Sets the swap fee.
* @param _swapFee The new swap fee.
Expand All @@ -86,6 +104,11 @@ contract Fly is ERC20, Ownable {
* @param _amount The amount of tokens to swap.
*/
function swap(bytes32 _recipient, uint256 _amount) public payable {
// check if the fly canister address is set
require(
fly_canister_address != address(0),
"Fly: fly canister address not set"
);
// check if the caller has enough tokens to swap
require(
balanceOf(msg.sender) >= _amount,
Expand Down
47 changes: 37 additions & 10 deletions ethereum/fly/test/Fly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ describe("Fly", () => {
const signer = await ethers.provider.getSigner(owner.address);

const Contract = await ethers.getContractFactory("Fly");
const contract = await Contract.deploy(
owner.address,
owner.address,
INITIAL_FEE
);
const contract = await Contract.deploy(owner.address, INITIAL_FEE);
await contract.waitForDeployment();

const address = await contract.getAddress();
Expand All @@ -52,19 +48,35 @@ describe("Fly", () => {
expect(await token.swapFee()).to.equal(INITIAL_FEE);
// check balance
expect(await token.balanceOf(owner.address)).to.equal(0);
// check fly canister is unset
expect(token.getFlyCanisterAddress()).to.be.revertedWith(
"Fly: fly canister address not set"
);
});

it("Should set fly canister address just once", async () => {
const { token, flyCanister } = deploy;
await token.setFlyCanisterAddress(flyCanister.address);
expect(await token.getFlyCanisterAddress()).to.equal(flyCanister.address);
expect(token.setFlyCanisterAddress(flyCanister.address)).to.be.revertedWith(
"Fly: fly canister address already set"
);
});

it("Should transcribe swap", async () => {
const { token, owner, flyCanister } = deploy;
const { token, owner } = deploy;
await token.setFlyCanisterAddress(owner.address);
await token.transcribeSwap(owner.address, 100);
expect(await token.balanceOf(owner.address)).to.equal(100);
});

it("Should swap 100 tokens", async () => {
const { token, owner } = deploy;
const { token, owner, flyCanister } = deploy;
await token.mintTestnetTokens(owner.address, 100);
const fee = await token.swapFee();

await token.setFlyCanisterAddress(flyCanister.address);

const initialBalance = await ethers.provider.getBalance(owner.address);

// swap and check event is emitted
Expand All @@ -85,10 +97,24 @@ describe("Fly", () => {
expect(finalBalance).to.greaterThan(fee);
});

it("should fail swap if fee is not paid", async () => {
it("should fail swap if fly canister address is not set", async () => {
const { token, owner } = deploy;
await token.mintTestnetTokens(owner.address, 100);

const fee = await token.swapFee();

expect(
token.swap(DUMMY_PRINCIPAL, 75, {
value: fee,
})
).to.be.revertedWith("Fly: fly canister address not set");
});

it("should fail swap if fee is not paid", async () => {
const { token, owner, flyCanister } = deploy;
await token.setFlyCanisterAddress(flyCanister.address);
await token.mintTestnetTokens(owner.address, 100);

await expect(
token.swap(DUMMY_PRINCIPAL, 75, {
value: 10,
Expand All @@ -99,7 +125,8 @@ describe("Fly", () => {
});

it("should fail swap if has not enough tokens", async () => {
const { token, owner } = deploy;
const { token, owner, flyCanister } = deploy;
await token.setFlyCanisterAddress(flyCanister.address);
await token.mintTestnetTokens(owner.address, 100);
const fee = await token.swapFee();

Expand Down Expand Up @@ -134,7 +161,7 @@ describe("Fly", () => {
});

it("should renounce ownership", async () => {
const { owner, token } = deploy;
const { token } = deploy;
await token.renounceOwnership();
expect(await token.owner()).to.equal(
"0x0000000000000000000000000000000000000000"
Expand Down

0 comments on commit 1eb4293

Please sign in to comment.