Skip to content

Commit

Permalink
feat: Sui support (#69)
Browse files Browse the repository at this point in the history
Co-authored-by: skosito <[email protected]>
  • Loading branch information
fadeev and skosito authored Feb 12, 2025
1 parent fe50cdb commit a46b56a
Show file tree
Hide file tree
Showing 24 changed files with 976 additions and 77 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,6 @@ tsconfig.tsbuildinfo

localnet.pid

test-ledger
test-ledger

localnet.json
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,16 @@
Localnet is a local development environment that simplifies the development of
universal apps.

Learn more about localnet:
## Dependencies

Make sure you have the following installed to use Localnet:

- [Foundry](https://getfoundry.sh/) `anvil` (required)
- Solana support: [`solana`](https://docs.solana.com/cli/install) and
`solana-test-validator`
- Sui support: [`sui`](https://docs.sui.io/install)

## Documentation

Learn more about Localnet:
https://www.zetachain.com/docs/developers/tutorials/localnet/
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "0.0.0-set-on-publish",
"description": "",
"scripts": {
"build": "del-cli dist && tsc && cpx 'packages/localnet/src/solana/**/*' dist/localnet/src/solana/",
"build": "del-cli dist && tsc && cpx 'packages/localnet/src/solana/**/*' dist/localnet/src/solana/ && cpx 'packages/localnet/src/sui/**/*' dist/localnet/src/sui/",
"lint:fix": "eslint --ext .js,.ts . --fix",
"lint": "eslint --ext .js,.ts ."
},
Expand All @@ -19,7 +19,9 @@
"types": "./dist/localnet/src/index.d.ts"
},
"./solana/deploy/gateway.so": "./dist/localnet/src/solana/deploy/gateway.so",
"./solana/deploy/gateway-keypair.json": "./dist/localnet/src/solana/deploy/gateway-keypair.json"
"./solana/deploy/gateway-keypair.json": "./dist/localnet/src/solana/deploy/gateway-keypair.json",
"./sui/gateway.mv": "./dist/localnet/src/sui/gateway.mv",
"./sui/gateway.json": "./dist/localnet/src/sui/gateway.json"
},
"keywords": [],
"author": "ZetaChain",
Expand Down Expand Up @@ -49,16 +51,19 @@
"dependencies": {
"@coral-xyz/anchor": "^0.30.1",
"@inquirer/prompts": "^5.5.0",
"@mysten/sui": "^0.0.0-experimental-20250131013137",
"@solana/web3.js": "^1.95.4",
"@uniswap/v2-core": "^1.0.1",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@zetachain/protocol-contracts": "11.0.0-rc5",
"ansis": "^3.3.2",
"bip39": "^3.1.0",
"bs58": "^6.0.0",
"concurrently": "^8.2.2",
"ed25519-hd-key": "^1.3.0",
"elliptic": "6.5.7",
"ethers": "^6.13.2",
"hardhat": "^2.22.8",
"wait-on": "^7.2.0"
}
}
}
5 changes: 3 additions & 2 deletions packages/localnet/src/createToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ export const createToken = async (
custody: any,
symbol: string,
isGasToken: boolean,
chainID: string
chainID: string,
decimals: number
) => {
let erc20;

Expand All @@ -35,7 +36,7 @@ export const createToken = async (
.deploy(
`ZRC-20 ${symbol} on ${chainID}`,
`ZRC20${symbol}`,
18,
decimals,
chainID,
isGasToken ? 1 : 2,
1,
Expand Down
60 changes: 54 additions & 6 deletions packages/localnet/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@ import { evmCall } from "./evmCall";
import { evmDeposit } from "./evmDeposit";
import { evmDepositAndCall } from "./evmDepositAndCall";
import { isSolanaAvailable } from "./isSolanaAvailable";
import { isSuiAvailable } from "./isSuiAvailable";
import { solanaDeposit } from "./solanaDeposit";
import { solanaDepositAndCall } from "./solanaDepositAndCall";
import { solanaSetup } from "./solanaSetup";
import { suiDeposit } from "./suiDeposit";
import { suiDepositAndCall } from "./suiDepositAndCall";
import { suiSetup } from "./suiSetup";
import { zetachainCall } from "./zetachainCall";
import { zetachainWithdraw } from "./zetachainWithdraw";
import { zetachainWithdrawAndCall } from "./zetachainWithdrawAndCall";
Expand Down Expand Up @@ -259,8 +263,9 @@ export const initLocalnet = async ({
exitOnError: boolean;
port: number;
}) => {
let solanaAddresses: any = [];
if (isSolanaAvailable()) {
solanaSetup({
solanaAddresses = solanaSetup({
handlers: {
deposit: (args: any) =>
solanaDeposit({
Expand Down Expand Up @@ -288,6 +293,39 @@ export const initLocalnet = async ({
console.error("Solana CLI not available. Skipping setup.");
}

let suiAddresses: any = [];

if (isSuiAvailable()) {
suiAddresses = suiSetup({
handlers: {
deposit: (args: any) => {
suiDeposit({
args,
asset: ethers.ZeroAddress,
chainID: "103",
deployer,
foreignCoins,
fungibleModuleSigner,
protocolContracts,
provider,
});
},
depositAndCall: (args: any) => {
suiDepositAndCall({
args,
asset: ethers.ZeroAddress,
chainID: "103",
deployer,
foreignCoins,
fungibleModuleSigner,
protocolContracts,
provider,
});
},
},
});
}

const provider = new ethers.JsonRpcProvider(`http://127.0.0.1:${port}`);
provider.pollingInterval = 100;
// anvil test mnemonic
Expand Down Expand Up @@ -333,11 +371,19 @@ export const initLocalnet = async ({
tss,
};

await createToken(addresses, contractsEthereum.custody, "ETH", true, "5");
await createToken(addresses, contractsEthereum.custody, "USDC", false, "5");
await createToken(addresses, contractsBNB.custody, "BNB", true, "97");
await createToken(addresses, contractsBNB.custody, "USDC", false, "97");
await createToken(addresses, null, "SOL", true, "901");
await createToken(addresses, contractsEthereum.custody, "ETH", true, "5", 18);
await createToken(
addresses,
contractsEthereum.custody,
"USDC",
false,
"5",
18
);
await createToken(addresses, contractsBNB.custody, "BNB", true, "97", 18);
await createToken(addresses, contractsBNB.custody, "USDC", false, "97", 18);
await createToken(addresses, null, "SOL", true, "901", 9);
await createToken(addresses, null, "SUI", true, "103", 9);

const evmContracts = {
5: contractsEthereum,
Expand Down Expand Up @@ -484,6 +530,8 @@ export const initLocalnet = async ({
);

return [
...(await suiAddresses),
...(await solanaAddresses),
...Object.entries(protocolContracts)
.filter(([_, value]) => value.target !== undefined)
.map(([key, value]) => {
Expand Down
10 changes: 10 additions & 0 deletions packages/localnet/src/isSuiAvailable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { execSync } from "child_process";

export const isSuiAvailable = (): boolean => {
try {
execSync("sui --version", { stdio: "ignore" });
return true;
} catch (error) {
return false;
}
};
2 changes: 1 addition & 1 deletion packages/localnet/src/log.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ansis from "ansis";

const chains: Record<string, { color: any; name: string }> = {
"102": { color: ansis.blue, name: "Sui" },
"103": { color: ansis.blue, name: "Sui" },
"5": { color: ansis.cyan, name: "Ethereum" },
"7001": { color: ansis.green, name: "ZetaChain" },
"901": { color: ansis.magenta, name: "Solana" },
Expand Down
7 changes: 2 additions & 5 deletions packages/localnet/src/solanaDeposit.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ansis from "ansis";
import { ethers } from "ethers";

import { logErr } from "./log";
import { log, logErr } from "./log";
import { solanaWithdraw } from "./solanaWithdraw";
import { zetachainDeposit } from "./zetachainDeposit";
import { zetachainSwapToCoverGas } from "./zetachainSwapToCoverGas";
Expand All @@ -17,9 +16,7 @@ export const solanaDeposit = async ({
}: any) => {
const [sender, , amount, asset] = args;
try {
console.log(
ansis.magenta(`[${ansis.bold("Solana")}]: Gateway Deposit executed`)
);
log("901", "Gateway Deposit executed");
let foreignCoin;
if (asset === ethers.ZeroAddress) {
foreignCoin = foreignCoins.find(
Expand Down
9 changes: 2 additions & 7 deletions packages/localnet/src/solanaDepositAndCall.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import ansis from "ansis";
import { ethers } from "ethers";

import { logErr } from "./log";
import { log, logErr } from "./log";
import { solanaWithdraw } from "./solanaWithdraw";
import { zetachainDepositAndCall } from "./zetachainDepositAndCall";
import { zetachainSwapToCoverGas } from "./zetachainSwapToCoverGas";
Expand All @@ -17,11 +16,7 @@ export const solanaDepositAndCall = async ({
}: any) => {
const [sender, , amount, asset] = args;
try {
console.log(
ansis.magenta(
`[${ansis.bold("Solana")}]: Gateway Deposit and call executed`
)
);
log("901", "Gateway Deposit and call executed");
let foreignCoin;
if (asset === ethers.ZeroAddress) {
foreignCoin = foreignCoins.find(
Expand Down
14 changes: 9 additions & 5 deletions packages/localnet/src/solanaSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export const solanaSetup = async ({ handlers }: any) => {
"@zetachain/localnet/solana/deploy/gateway-keypair.json"
);

const gatewayProgram = new anchor.Program(Gateway_IDL as anchor.Idl);

try {
if (!fs.existsSync(gatewayKeypairPath)) {
throw new Error(`Keypair file not found: ${gatewayKeypairPath}`);
Expand All @@ -64,7 +66,6 @@ export const solanaSetup = async ({ handlers }: any) => {
const address = addressBuffer.slice(-20);
const tssAddress = Array.from(address);

const gatewayProgram = new anchor.Program(Gateway_IDL as anchor.Idl);
const connection = gatewayProgram.provider.connection;

// Airdrop into the payer so it has enough SOL
Expand Down Expand Up @@ -126,16 +127,19 @@ export const solanaSetup = async ({ handlers }: any) => {
}
throw error;
}
return [
{
address: gatewayProgram.programId.toBase58(),
chain: "solana",
type: "gatewayProgram",
},
];
};

export const solanaMonitorTransactions = async ({ handlers }: any) => {
const gatewayProgram = new anchor.Program(Gateway_IDL as anchor.Idl);
const connection = gatewayProgram.provider.connection;

console.log(
`Monitoring new transactions for program: ${gatewayProgram.programId.toBase58()}`
);

let lastSignature: string;

setInterval(async () => {
Expand Down
23 changes: 8 additions & 15 deletions packages/localnet/src/solanaWithdraw.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as anchor from "@coral-xyz/anchor";
import { PublicKey } from "@solana/web3.js";
import ansis from "ansis";
import bs58 from "bs58";
import { keccak256 } from "ethereumjs-util";
import { ethers } from "ethers";

import { log, logErr } from "./log";
import Gateway_IDL from "./solana/idl/gateway.json";
import { payer, tssKeyPair } from "./solanaSetup";

Expand Down Expand Up @@ -50,21 +50,14 @@ export const solanaWithdraw = async (recipient: string, amount: bigint) => {
recipient: new PublicKey(recipient),
})
.rpc();
console.log(
ansis.magenta(
`[${ansis.bold(
"Solana"
)}]: Executing Gateway withdraw, sending ${ethers.formatUnits(
amount,
9
)} SOL to ${recipient}`
)
log(
"901",
`Executing Gateway withdraw, sending ${ethers.formatUnits(
amount,
9
)} SOL to ${recipient}`
);
} catch (err) {
console.error(
ansis.red(
`[${ansis.bold("Solana")}]: Error executing Gateway withdraw, ${err}`
)
);
logErr("901", `Error executing Gateway withdraw, ${err}`);
}
};
43 changes: 43 additions & 0 deletions packages/localnet/src/sui/gateway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"modules": [
"oRzrCwYAAAANAQAWAhZGA1yIAgTkAjoFngOjAgfBBaoGCOsLYAbLDGQKrw1NC/wNAgz+DeQGDeIUDg7wFAQAJQEUAT8CFQIWAhoCJAIzAjsCPQI+AA0EAQABAAYIAAAPDAAADgwAAAAMAAAFAwAABAMAAQkHAAILBwADAQwABAIEAQABBQMMAQABBwcHAAcMBAAICAIACgoCAAAoAAEAAB4CAQEAAB8DAQEAAEoEAQEAAEcFAQEAAEIGAQEAAC0HAQAANAYBAABABgEAACECAQEAACADAQEAABkIAQEAAEsJCgEAAEgFAQEAAEMGAQEAAC4HCwAANQYBAABBBgEAADIMDQAAEQwOAAAQDA4AAEUMDQEAACsMDwAALAwPAQAAGwEQAQABMCMNAAImATEBAAIqMRAAAxIsAQIHBAMXKy8CBwQDGCUmAgcEAx0rDwIHBAMxABMABC8oDQEABEQwDQEABEwBJwEABSkKJwEABTwpCgEABUQfDQEABiMdAQEDBycVDgEIBzEAEgAJNxoBAQwJOh0BAQgJPRoBAQgKORgZACgUKBYNFywULBYsGyscCR0KHQwdKgoNHQ4dJh0YHQsdJyAnIhcdHiQkHSEdJR0fJCMdHCQdJCIdGh0BBwgPAAQHCAELCwEJAAgHBwgPBQcIAQsLAQkACAcKAgcIDwYHCAEDAwUGCAIHCA8CBwgBBggDAgcIAQYIBAMHCAEGCAQHCA8DBwgBCwsBCQAIBwUHCAEDAwYIAgcIDwELCwEJAAIIAggDAQYIAQEDAQgMAQEBCAcECAQIAQgDCAIBCA0BCAkBCAIBBgkAAQgDAQgOAQYIDwEFAgkABQEIBAEIAQEJAAIDCAcBBgsLAQkAAQgFAQIBCAYBBggHAggHCwABCQACBwgJCQABBwkBAQsKAQkAAgcLCgEJAAsKAQkAAwcLCgEJAAMHCA8DBwsAAQkACwABCQAIBwIGCAkJAAMHCAkJAAkBAQcLAAEJAAIIAwgCAQYJAQEGCwoBCQABCAgIQWRtaW5DYXADQmFnB0JhbGFuY2UEQ29pbhNEZXBvc2l0QW5kQ2FsbEV2ZW50DERlcG9zaXRFdmVudAdHYXRld2F5AklEA1NVSQZTdHJpbmcJVHhDb250ZXh0CFR5cGVOYW1lA1VJRAVWYXVsdAxXaGl0ZWxpc3RDYXALV2l0aGRyYXdDYXAUYWN0aXZlX3doaXRlbGlzdF9jYXATYWN0aXZlX3dpdGhkcmF3X2NhcANhZGQGYW1vdW50BWFzY2lpA2JhZwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0I2NoZWNrX3JlY2VpdmVyX2FuZF9kZXBvc2l0X3RvX3ZhdWx0BGNvaW4JY29pbl9uYW1lCWNvaW5fdHlwZRJjb250YWluc193aXRoX3R5cGUHZGVwb3NpdBBkZXBvc2l0X2FuZF9jYWxsFWRlcG9zaXRfYW5kX2NhbGxfaW1wbAxkZXBvc2l0X2ltcGwOZGVwb3NpdF9wYXVzZWQEZW1pdAVldmVudAdnYXRld2F5A2dldAJpZARpbml0DGludG9fYmFsYW5jZQtpbnRvX3N0cmluZwlpc19wYXVzZWQOaXNfd2hpdGVsaXN0ZWQgaXNzdWVfd2l0aGRyYXdfYW5kX3doaXRlbGlzdF9jYXAlaXNzdWVfd2l0aGRyYXdfYW5kX3doaXRlbGlzdF9jYXBfaW1wbARqb2luBmxlbmd0aANuZXcFbm9uY2UGb2JqZWN0BXBhdXNlCnBhdXNlX2ltcGwHcGF5bG9hZA9wdWJsaWNfdHJhbnNmZXIIcmVjZWl2ZXIGc2VuZGVyDHNoYXJlX29iamVjdANzdWkEdGFrZQh0cmFuc2Zlcgp0eF9jb250ZXh0CXR5cGVfbmFtZQd1bnBhdXNlDHVucGF1c2VfaW1wbAt1bndoaXRlbGlzdBB1bndoaXRlbGlzdF9pbXBsBXZhbHVlDXZhdWx0X2JhbGFuY2UGdmF1bHRzCXdoaXRlbGlzdA53aGl0ZWxpc3RfaW1wbAt3aGl0ZWxpc3RlZAh3aXRoZHJhdw13aXRoZHJhd19pbXBsBHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAADCAYAAAAAAAAAAwgHAAAAAAAAAAMIKgAAAAAAAAADCAAEAAAAAAAAAAICFgsKAQkASQEBAgYnCA1GCAkyAxEIDBAIDCIBAgIBJwgNAwIBJwgNBAIBJwgNBQIEHAgHEwM5BTgIBwYCBRwIBxMDOQU4CAc2CgIAHQAAAAARLQoAESkSAgwECgARKRIDDAMKABEpEgQMAQoAESkKABEgBgAAAAAAAAAADgQ4AA4DOAEJEgEMAg0CDgM4AgsECgAuES04AwsDCgAuES04BAsBCwAuES04BQsCOAYCAQAEAAEGCwALAQsCCwM4BwICAAQAAQcLAAsBCwILAwsEOAgCAwAEAAEJCwALAQsCCwQLBTgJCwM4CgIEAAQAAQQLAAsBOAsCBQAEAAEECwALATgMAgYABAAWDwsACwEKAhEPDAMKAi4RLTgDCwMLAi4RLTgEAgcABAABBAsACwEREAIIAAQAAQQLAAsBERECCQEAAB4SDgE4DQwEOA4MBQsACwEKAjgPCwULBAsDLhEtCwISBTgQAgoBAAAeHw4DQSEHCSUEBgUMCwABCwQBBwQnDgE4DQwFOA4MBgsACwEKAjgPCwYLBQsELhEtCwILAxIGOBECCwAAABApDgIRGQcIIQQGBQoLAAEHAScKAC44EgQPBRMLAAEHAicKABAAFCAEGQUdCwABBwcnOA4MAwsADwELAzgTNgALATgUOBUBAgwBAAAQNwoAEAMUCwM4ACEECAUOCwABCwQBBwUnCgAuOBIEEwUZCwABCwQBBwInCgIKABAEFCEEIAUmCwABCwQBBwMnCwIGAQAAAAAAAAAWCgAPBBU4DgwFCwAPAQsFOBM2AAsBCwQ4FgINAQAAKjIKABAFFAsBOAEhBAgFDAsAAQcGJwoALjgSCSEEEwUXCwABBwAnCgAQATgOOBcEJgsADwE4DjgTDAIICwI2ARUFMTgODAQ4GAg5AAwDCwAPAQsECwM4GQIOAQAALRMKAC44EgQFBQkLAAEHAicLAA8BOA44EwwCCQsCNgEVAg8BAAAuFQoCESkSAgwECwIRKRIDDAMOBDgACgAPAxUOAzgBCwAPBRULBAsDAhABAAABBQgLAA8AFQIRAQAAAQUJCwAPABUCEgEAAAEECwAQBBQCEwEAAAEECwAQAxQCFAEAAAEECwAQBRQCFQEAABARCgA4EiAECAsAAQYAAAAAAAAAAAI4DgwBCwAQAQsBOBo3ADgbAhYBAAABBAsAEAAUAhcBAAAQEzgODAEKABABCgE4FyAEDAsAAQkCCwAQAQsBOBo3ARQCGAAAAAEDOBwRGwIBBQEBAAABAwECAQQAAQIdBh0A"
],
"dependencies": [
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000002"
],
"digest": [
24,
197,
179,
174,
252,
181,
245,
112,
22,
199,
43,
196,
245,
250,
202,
55,
65,
233,
222,
174,
240,
245,
63,
115,
106,
68,
34,
50,
148,
63,
189,
57
]
}
Loading

0 comments on commit a46b56a

Please sign in to comment.