diff --git a/src/pages/developers/standards/nft.mdx b/src/pages/developers/standards/nft.mdx index f81988f3..1e36cf65 100644 --- a/src/pages/developers/standards/nft.mdx +++ b/src/pages/developers/standards/nft.mdx @@ -59,12 +59,11 @@ RPC_BASE=$(zetachain q chains show --chain-id 84532 -f rpc) RPC_ZETACHAIN=$(zetachain q chains show --chain-id 7001 -f rpc) ZRC20_ETHEREUM=$(zetachain q tokens show -s ETH.ETHSEP -f zrc20) -ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20) +ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20) GATEWAY_ETHEREUM=0x0c487a766110c85d301d96e33579c5b317fa4995 GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995 GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7 -UNISWAP_ROUTER=0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe GAS_LIMIT=1000000 ``` @@ -80,8 +79,6 @@ NFT_ZETACHAIN=$(npx tsx commands deploy \ --rpc $RPC_ZETACHAIN \ --private-key $PRIVATE_KEY \ --name ZetaChainUniversalNFT \ - --uniswap-router $UNISWAP_ROUTER \ - --gateway $GATEWAY_ZETACHAIN \ --gas-limit $GAS_LIMIT | jq -r .contractAddress) && echo $NFT_ZETACHAIN ``` diff --git a/src/pages/developers/standards/token.mdx b/src/pages/developers/standards/token.mdx index 65cf62f4..e55223e3 100644 --- a/src/pages/developers/standards/token.mdx +++ b/src/pages/developers/standards/token.mdx @@ -58,12 +58,11 @@ RPC_BASE=$(zetachain q chains show --chain-id 84532 -f rpc) RPC_ZETACHAIN=$(zetachain q chains show --chain-id 7001 -f rpc) ZRC20_ETHEREUM=$(zetachain q tokens show -s ETH.ETHSEP -f zrc20) -ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20) +ZRC20_BASE=$(zetachain q tokens show -s ETH.BASESEP -f zrc20) GATEWAY_ETHEREUM=0x0c487a766110c85d301d96e33579c5b317fa4995 GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995 GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7 -UNISWAP_ROUTER=0x2ca7d64A7EFE2D62A725E2B35Cf7230D6677FfEe GAS_LIMIT=1000000 ``` @@ -79,8 +78,6 @@ ZETACHAIN_TOKEN=$(npx tsx commands deploy \ --rpc $RPC_ZETACHAIN \ --private-key $PRIVATE_KEY \ --name ZetaChainUniversalToken \ - --uniswap-router $UNISWAP_ROUTER \ - --gateway $GATEWAY_ZETACHAIN \ --gas-limit $GAS_LIMIT | jq -r .contractAddress) && echo $ZETACHAIN_TOKEN ``` diff --git a/src/pages/developers/tutorials/call.mdx b/src/pages/developers/tutorials/call.mdx index 1086df8c..c861a299 100644 --- a/src/pages/developers/tutorials/call.mdx +++ b/src/pages/developers/tutorials/call.mdx @@ -88,10 +88,13 @@ function onCall( ### Making outgoing calls -To call a contract on a connected chain from your Universal App, you must first -approve the Gateway to spend the gas token: +To call a contract on a connected chain from your Universal App, first quote the +destination gas fee for your intended gas limit, pull that fee from the caller, +and approve the Gateway to spend it: ```solidity +(, uint256 gasFee) = IZRC20(zrc20).withdrawGasFeeWithGasLimit(callOptions.gasLimit); +IZRC20(zrc20).transferFrom(msg.sender, address(this), gasFee); IZRC20(zrc20).approve(address(gateway), gasFee); ``` @@ -267,7 +270,6 @@ your connected EVM testnet (for example, Base Sepolia) and the Gateway addresses for each chain. ```bash -GATEWAY_ZETACHAIN=0x6c533f7fe93fae114d0954697069df33c9b74fd7 GATEWAY_BASE=0x0c487a766110c85d301d96e33579c5b317fa4995 RPC_ZETACHAIN=https://zetachain-athens-evm.blockpi.network/v1/rpc/public @@ -281,8 +283,7 @@ UNIVERSAL=$(forge create Universal \ --rpc-url $RPC_ZETACHAIN \ --private-key $PRIVATE_KEY \ --broadcast \ - --json \ - --constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL + --json | jq -r .deployedTo) && echo $UNIVERSAL ``` ### Deploy Connected to Base Sepolia @@ -451,10 +452,9 @@ from the localnet registry: ```bash RPC=http://localhost:8545 -ZRC20_ETHEREUM=$(jq -r '."11155112".chainInfo.gasZRC20' ~/.zetachain/localnet/registry.json) && echo $ZRC20_ETHEREUM +ZRC20_ETHEREUM=$(jq -r '."11155112".zrc20Tokens[] | select(.coinType == "gas" and .originChainId == "11155112") | .address' ~/.zetachain/localnet/registry.json) && echo $ZRC20_ETHEREUM PRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEY GATEWAY_ETHEREUM=$(jq -r '.["11155112"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ETHEREUM -GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAIN ``` Deploy the Universal App: @@ -464,8 +464,7 @@ UNIVERSAL=$(forge create Universal \ --rpc-url $RPC \ --private-key $PRIVATE_KEY \ --broadcast \ - --json \ - --constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL + --json | jq -r .deployedTo) && echo $UNIVERSAL ``` Deploy the Connected contract: diff --git a/src/pages/developers/tutorials/hello.mdx b/src/pages/developers/tutorials/hello.mdx index 2f3be81c..92a0ee53 100644 --- a/src/pages/developers/tutorials/hello.mdx +++ b/src/pages/developers/tutorials/hello.mdx @@ -57,22 +57,10 @@ A Universal App is a contract that implements the `UniversalContract` interface. // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import "@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM.sol"; +import "@zetachain/protocol-contracts/contracts/zevm/interfaces/UniversalContract.sol"; contract Universal is UniversalContract { - GatewayZEVM public immutable gateway; - event HelloEvent(string, string); - error Unauthorized(); - - modifier onlyGateway() { - if (msg.sender != address(gateway)) revert Unauthorized(); - _; - } - - constructor(address payable gatewayAddress) { - gateway = GatewayZEVM(gatewayAddress); - } function onCall( MessageContext calldata context, @@ -86,10 +74,6 @@ contract Universal is UniversalContract { } ``` -The constructor takes ZetaChain’s Gateway address and stores it in a state -variable. The Gateway is used for making outbound contract calls and token -withdrawals. - A universal contract must implement the `onCall` function. This function is triggered when the contract receives a call from a connected chain via the Gateway. The function processes incoming data, which includes: @@ -109,7 +93,8 @@ In this example, `onCall` decodes the message into a string and emits an event. `onCall` should only be called by the Gateway to ensure that it is only called as a response to a call on a connected chain and that you can trust the values -of the function parameters. +of the function parameters. This is enforced by the `onlyGateway` modifier, +which is inherited from `UniversalContract`. ## Option 1: Deploy on Localnet @@ -160,39 +145,13 @@ The `forge build` command tells Foundry to compile all Solidity smart contracts within your project, ensuring you're working with the latest compiled versions. Successful compilation will generate bytecode for your contracts. -To deploy and interact with contracts on the ZetaChain localnet, you'll need the -address of the ZetaChain Gateway contract. This contract acts as the entry point -for cross-chain interactions on ZetaChain. - -- While your localnet is still running in its dedicated terminal, carefully - examine its output. -- Copy the Gateway contract address from the localnet terminal output. Look for - the row labeled `gateway` under the `ZETACHAIN` section. It will typically - appear in a format similar to this: - -``` -| gateway │ '0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6' | -``` - -Copy only the hexadecimal address (e.g., -`0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6`). Do not include the single quotes -or any other surrounding text. - -Alternatively, run the following command to get the Gateway address -programmatically: - -``` -GATEWAY_ZETACHAIN=$(jq -r '.["31337"].contracts[] | select(.contractType == "gateway") | .address' ~/.zetachain/localnet/registry.json) && echo $GATEWAY_ZETACHAIN -``` - Fetch a private key with pre-funded tokens on the connected chain: ``` PRIVATE_KEY=$(jq -r '.private_keys[0]' ~/.zetachain/localnet/anvil.json) && echo $PRIVATE_KEY ``` -Deploy the universal contract and provide the Gateway address in the -constructor: +Deploy the universal contract: ``` UNIVERSAL=$(forge create Universal \ @@ -200,8 +159,7 @@ UNIVERSAL=$(forge create Universal \ --private-key $PRIVATE_KEY \ --evm-version paris \ --broadcast \ - --json \ - --constructor-args $GATEWAY_ZETACHAIN | jq -r .deployedTo) && echo $UNIVERSAL + --json | jq -r .deployedTo) && echo $UNIVERSAL ``` ### Make a Call to the Universal App @@ -263,16 +221,14 @@ string. ### Deploy the Contract on ZetaChain -Deploy the contract to ZetaChain’s testnet using the Gateway address from the -[Contract Addresses page](/reference/network/contracts/): +Deploy the contract to ZetaChain’s testnet: ``` UNIVERSAL=$(forge create Universal \ --rpc-url https://zetachain-athens-evm.blockpi.network/v1/rpc/public \ --private-key $PRIVATE_KEY \ --broadcast \ - --json \ - --constructor-args 0x6c533f7fe93fae114d0954697069df33c9b74fd7 | jq -r .deployedTo) + --json | jq -r .deployedTo) ``` ### Call a Universal Contract from Base diff --git a/src/pages/developers/tutorials/swap.mdx b/src/pages/developers/tutorials/swap.mdx index 1257e751..0c59ca25 100644 --- a/src/pages/developers/tutorials/swap.mdx +++ b/src/pages/developers/tutorials/swap.mdx @@ -32,8 +32,8 @@ The Swap contract performs the following steps: 3. Queries the withdrawal gas fee required to send the target token back to the destination chain. -4. Swaps a portion of the incoming tokens for ZRC-20 gas tokens to cover the - withdrawal fee using Uniswap v2 pools. +4. If withdrawing to a connected chain, swaps a portion of the incoming tokens + for ZRC-20 gas tokens to cover the withdrawal fee using Uniswap v2 pools. 5. Swaps the remaining balance into the target token. 6. Withdraws the swapped tokens to the recipient on the destination chain. @@ -93,7 +93,7 @@ to perform token swaps across blockchains with a single cross-chain call. Tokens are received as ZRC-20s, optionally swapped using Uniswap v2 liquidity, and withdrawn back to a connected chain. -### Universal App entrypoint: on_call +### Universal App entrypoint: onCall The contract is deployed on ZetaChain and implements `UniversalContract`, exposing a single entrypoint. Cross-chain deliveries are executed only via the @@ -145,6 +145,10 @@ gateway.withdraw( ); ``` +If the destination gas token (`gasZRC20`) is the same as the target token, the +contract approves a single combined allowance of `out + gasFee` for the Gateway +instead of two separate approvals. + ### Funding destination execution from the user’s input The app provisions destination gas out of the input, so users don’t need to @@ -260,6 +264,9 @@ SwapHelperLib.swapExactTokensForTokens( ); ``` +Note: If `inputToken == gasZRC20`, the contract uses `gasFee` directly (no swap +for gas is required) and only swaps the remaining balance to the target token. + You’re free to replace uniswapRouter and the helper calls with any DEX interface or custom routing logic—only the ZRC-20 token flow and the Gateway withdraw semantics are assumed by the rest of the contract.