Skip to content

Commit

Permalink
feat: connector native minting cap (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito authored Jul 25, 2024
1 parent 9b5ff23 commit 0ba9959
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 8 deletions.
30 changes: 26 additions & 4 deletions contracts/prototypes/evm/ZetaConnectorNonNative.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,38 @@ import "./IZetaNonEthNew.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

contract ZetaConnectorNonNative is ZetaConnectorNewBase {
/// @notice Max supply for minting
uint256 public maxSupply = type(uint256).max;

/// @notice Event triggered when max supply is updated
/// @param maxSupply New max supply
event MaxSupplyUpdated(uint256 maxSupply);
error ExceedsMaxSupply();

constructor(address _gateway, address _zetaToken, address _tssAddress)
ZetaConnectorNewBase(_gateway, _zetaToken, _tssAddress)
{}

// @dev withdraw is called by TSS address, it mints zetaToken to the destination address
/// @notice Set max supply for minting
/// @param _maxSupply New max supply
/// @dev Caller must be TSS
function setMaxSupply(uint256 _maxSupply) external onlyTSS() {
maxSupply = _maxSupply;
emit MaxSupplyUpdated(_maxSupply);
}

/// @dev withdraw is called by TSS address, it mints zetaToken to the destination address
function withdraw(address to, uint256 amount, bytes32 internalSendHash) external override nonReentrant onlyTSS {
if (amount + IERC20(zetaToken).totalSupply() > maxSupply) revert ExceedsMaxSupply();

IZetaNonEthNew(zetaToken).mint(to, amount, internalSendHash);
emit Withdraw(to, amount);
}

// @dev withdrawAndCall is called by TSS address, it mints zetaToken and calls a contract
/// @dev withdrawAndCall is called by TSS address, it mints zetaToken and calls a contract
function withdrawAndCall(address to, uint256 amount, bytes calldata data, bytes32 internalSendHash) external override nonReentrant onlyTSS {
if (amount + IERC20(zetaToken).totalSupply() > maxSupply) revert ExceedsMaxSupply();

// Mint zetaToken to the Gateway contract
IZetaNonEthNew(zetaToken).mint(address(gateway), amount, internalSendHash);

Expand All @@ -27,8 +47,10 @@ contract ZetaConnectorNonNative is ZetaConnectorNewBase {
emit WithdrawAndCall(to, amount, data);
}

// @dev withdrawAndRevert is called by TSS address, it mints zetaToken to the gateway and calls onRevert on a contract
/// @dev withdrawAndRevert is called by TSS address, it mints zetaToken to the gateway and calls onRevert on a contract
function withdrawAndRevert(address to, uint256 amount, bytes calldata data, bytes32 internalSendHash) external override nonReentrant onlyTSS {
if (amount + IERC20(zetaToken).totalSupply() > maxSupply) revert ExceedsMaxSupply();

// Mint zetaToken to the Gateway contract
IZetaNonEthNew(zetaToken).mint(address(gateway), amount, internalSendHash);

Expand All @@ -38,7 +60,7 @@ contract ZetaConnectorNonNative is ZetaConnectorNewBase {
emit WithdrawAndRevert(to, amount, data);
}

// @dev receiveTokens handles token transfer and burn them
/// @dev receiveTokens handles token transfer and burn them
function receiveTokens(uint256 amount) external override {
IZetaNonEthNew(zetaToken).burnFrom(msg.sender, amount);
}
Expand Down

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions testFoundry/GatewayEVM.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ contract GatewayEVMTest is Test, IGatewayEVMErrors, IGatewayEVMEvents, IReceiver

token.mint(owner, 1000000);
token.transfer(address(custody), 500000);

vm.deal(tssAddress, 1 ether);
}

function testForwardCallToReceiveNonPayable() public {
Expand Down
2 changes: 2 additions & 0 deletions testFoundry/ZetaConnectorNative.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ contract ZetaConnectorNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvents,
vm.stopPrank();

zetaToken.mint(address(zetaConnector), 5000000);

vm.deal(tssAddress, 1 ether);
}

function testWithdraw() public {
Expand Down
56 changes: 55 additions & 1 deletion testFoundry/ZetaConnectorNonNative.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
address destination;
address tssAddress;

error ExceedsMaxSupply();
event MaxSupplyUpdated(uint256 maxSupply);

function setUp() public {
owner = address(this);
destination = address(0x1234);
Expand All @@ -58,6 +61,8 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
gateway.setCustody(address(custody));
gateway.setConnector(address(zetaConnector));
vm.stopPrank();

vm.deal(tssAddress, 1 ether);
}

function testWithdraw() public {
Expand All @@ -76,6 +81,23 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
assertEq(balanceAfter, amount);
}

function testWithdrawFailsIfMaxSupplyReached() public {
uint256 amount = 100000;
uint256 balanceBefore = zetaToken.balanceOf(destination);
assertEq(balanceBefore, 0);
bytes32 internalSendHash = "";

uint256 maxSupply = 90000;
vm.expectEmit(true, true, true, true, address(zetaConnector));
emit MaxSupplyUpdated(maxSupply);
vm.prank(tssAddress);
zetaConnector.setMaxSupply(maxSupply);

vm.prank(tssAddress);
vm.expectRevert(ExceedsMaxSupply.selector);
zetaConnector.withdraw(destination, amount, internalSendHash);
}

function testWithdrawFailsIfSenderIsNotTSS() public {
uint256 amount = 100000;
bytes32 internalSendHash = "";
Expand Down Expand Up @@ -130,6 +152,22 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
zetaConnector.withdrawAndCall(address(receiver), amount, data, internalSendHash);
}

function testWithdrawAndCallReceiveERC20FailsIfMaxSupplyReached() public {
uint256 amount = 100000;
bytes32 internalSendHash = "";
bytes memory data = abi.encodeWithSignature("receiveERC20(uint256,address,address)", amount, address(zetaToken), destination);

uint256 maxSupply = 90000;
vm.expectEmit(true, true, true, true, address(zetaConnector));
emit MaxSupplyUpdated(maxSupply);
vm.prank(tssAddress);
zetaConnector.setMaxSupply(maxSupply);

vm.prank(tssAddress);
vm.expectRevert(ExceedsMaxSupply.selector);
zetaConnector.withdrawAndCall(address(receiver), amount, data, internalSendHash);
}

function testWithdrawAndCallReceiveNoParams() public {
uint256 amount = 100000;
bytes32 internalSendHash = "";
Expand Down Expand Up @@ -237,7 +275,7 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
assertEq(balanceGateway, 0);
}

function testWithdrawAndRevertFailsIfSenderIsNotTSS() public {
function testWithdrawAndRevertFailsIfSenderIsNotTSS() public {
uint256 amount = 100000;
bytes32 internalSendHash = "";
bytes memory data = abi.encodePacked("hello");
Expand All @@ -246,4 +284,20 @@ contract ZetaConnectorNonNativeTest is Test, IGatewayEVMErrors, IGatewayEVMEvent
vm.expectRevert(InvalidSender.selector);
zetaConnector.withdrawAndRevert(address(receiver), amount, data, internalSendHash);
}

function testWithdrawAndRevertFailsIfMaxSupplyReached() public {
uint256 amount = 100000;
bytes32 internalSendHash = "";
bytes memory data = abi.encodePacked("hello");

uint256 maxSupply = 90000;
vm.expectEmit(true, true, true, true, address(zetaConnector));
emit MaxSupplyUpdated(maxSupply);
vm.prank(tssAddress);
zetaConnector.setMaxSupply(maxSupply);

vm.prank(tssAddress);
vm.expectRevert(ExceedsMaxSupply.selector);
zetaConnector.withdrawAndRevert(address(receiver), amount, data, internalSendHash);
}
}
65 changes: 65 additions & 0 deletions typechain-types/contracts/prototypes/evm/ZetaConnectorNonNative.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ import type {
export interface ZetaConnectorNonNativeInterface extends utils.Interface {
functions: {
"gateway()": FunctionFragment;
"maxSupply()": FunctionFragment;
"receiveTokens(uint256)": FunctionFragment;
"setMaxSupply(uint256)": FunctionFragment;
"tssAddress()": FunctionFragment;
"withdraw(address,uint256,bytes32)": FunctionFragment;
"withdrawAndCall(address,uint256,bytes,bytes32)": FunctionFragment;
Expand All @@ -41,7 +43,9 @@ export interface ZetaConnectorNonNativeInterface extends utils.Interface {
getFunction(
nameOrSignatureOrTopic:
| "gateway"
| "maxSupply"
| "receiveTokens"
| "setMaxSupply"
| "tssAddress"
| "withdraw"
| "withdrawAndCall"
Expand All @@ -50,10 +54,15 @@ export interface ZetaConnectorNonNativeInterface extends utils.Interface {
): FunctionFragment;

encodeFunctionData(functionFragment: "gateway", values?: undefined): string;
encodeFunctionData(functionFragment: "maxSupply", values?: undefined): string;
encodeFunctionData(
functionFragment: "receiveTokens",
values: [PromiseOrValue<BigNumberish>]
): string;
encodeFunctionData(
functionFragment: "setMaxSupply",
values: [PromiseOrValue<BigNumberish>]
): string;
encodeFunctionData(
functionFragment: "tssAddress",
values?: undefined
Expand Down Expand Up @@ -87,10 +96,15 @@ export interface ZetaConnectorNonNativeInterface extends utils.Interface {
encodeFunctionData(functionFragment: "zetaToken", values?: undefined): string;

decodeFunctionResult(functionFragment: "gateway", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "maxSupply", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "receiveTokens",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setMaxSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "tssAddress", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
decodeFunctionResult(
Expand All @@ -104,16 +118,29 @@ export interface ZetaConnectorNonNativeInterface extends utils.Interface {
decodeFunctionResult(functionFragment: "zetaToken", data: BytesLike): Result;

events: {
"MaxSupplyUpdated(uint256)": EventFragment;
"Withdraw(address,uint256)": EventFragment;
"WithdrawAndCall(address,uint256,bytes)": EventFragment;
"WithdrawAndRevert(address,uint256,bytes)": EventFragment;
};

getEvent(nameOrSignatureOrTopic: "MaxSupplyUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Withdraw"): EventFragment;
getEvent(nameOrSignatureOrTopic: "WithdrawAndCall"): EventFragment;
getEvent(nameOrSignatureOrTopic: "WithdrawAndRevert"): EventFragment;
}

export interface MaxSupplyUpdatedEventObject {
maxSupply: BigNumber;
}
export type MaxSupplyUpdatedEvent = TypedEvent<
[BigNumber],
MaxSupplyUpdatedEventObject
>;

export type MaxSupplyUpdatedEventFilter =
TypedEventFilter<MaxSupplyUpdatedEvent>;

export interface WithdrawEventObject {
to: string;
amount: BigNumber;
Expand Down Expand Up @@ -179,11 +206,18 @@ export interface ZetaConnectorNonNative extends BaseContract {
functions: {
gateway(overrides?: CallOverrides): Promise<[string]>;

maxSupply(overrides?: CallOverrides): Promise<[BigNumber]>;

receiveTokens(
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

setMaxSupply(
_maxSupply: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

tssAddress(overrides?: CallOverrides): Promise<[string]>;

withdraw(
Expand Down Expand Up @@ -214,11 +248,18 @@ export interface ZetaConnectorNonNative extends BaseContract {

gateway(overrides?: CallOverrides): Promise<string>;

maxSupply(overrides?: CallOverrides): Promise<BigNumber>;

receiveTokens(
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

setMaxSupply(
_maxSupply: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<ContractTransaction>;

tssAddress(overrides?: CallOverrides): Promise<string>;

withdraw(
Expand Down Expand Up @@ -249,11 +290,18 @@ export interface ZetaConnectorNonNative extends BaseContract {
callStatic: {
gateway(overrides?: CallOverrides): Promise<string>;

maxSupply(overrides?: CallOverrides): Promise<BigNumber>;

receiveTokens(
amount: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<void>;

setMaxSupply(
_maxSupply: PromiseOrValue<BigNumberish>,
overrides?: CallOverrides
): Promise<void>;

tssAddress(overrides?: CallOverrides): Promise<string>;

withdraw(
Expand Down Expand Up @@ -283,6 +331,9 @@ export interface ZetaConnectorNonNative extends BaseContract {
};

filters: {
"MaxSupplyUpdated(uint256)"(maxSupply?: null): MaxSupplyUpdatedEventFilter;
MaxSupplyUpdated(maxSupply?: null): MaxSupplyUpdatedEventFilter;

"Withdraw(address,uint256)"(
to?: PromiseOrValue<string> | null,
amount?: null
Expand Down Expand Up @@ -318,11 +369,18 @@ export interface ZetaConnectorNonNative extends BaseContract {
estimateGas: {
gateway(overrides?: CallOverrides): Promise<BigNumber>;

maxSupply(overrides?: CallOverrides): Promise<BigNumber>;

receiveTokens(
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;

setMaxSupply(
_maxSupply: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<BigNumber>;

tssAddress(overrides?: CallOverrides): Promise<BigNumber>;

withdraw(
Expand Down Expand Up @@ -354,11 +412,18 @@ export interface ZetaConnectorNonNative extends BaseContract {
populateTransaction: {
gateway(overrides?: CallOverrides): Promise<PopulatedTransaction>;

maxSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;

receiveTokens(
amount: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;

setMaxSupply(
_maxSupply: PromiseOrValue<BigNumberish>,
overrides?: Overrides & { from?: PromiseOrValue<string> }
): Promise<PopulatedTransaction>;

tssAddress(overrides?: CallOverrides): Promise<PopulatedTransaction>;

withdraw(
Expand Down
Loading

0 comments on commit 0ba9959

Please sign in to comment.