342 lines
13 KiB
JavaScript
342 lines
13 KiB
JavaScript
import { exec } from "child_process";
|
|
import fs from "fs";
|
|
import { BigNumber, ethers } from "ethers";
|
|
import {
|
|
getEmitterAddressEth,
|
|
parseSequenceFromLogEth,
|
|
attestFromEth,
|
|
tryNativeToHexString,
|
|
} from "@certusone/wormhole-sdk";
|
|
|
|
import fetch from "node-fetch";
|
|
|
|
async function main() {
|
|
let config = JSON.parse(fs.readFileSync("./xdapp.config.json").toString());
|
|
let network = config.networks[process.argv[2]];
|
|
|
|
let deployment;
|
|
try {
|
|
deployment = JSON.parse(fs.readFileSync("./deployment.json").toString());
|
|
} catch (e) {
|
|
deployment = {};
|
|
deployment[[process.argv[2]]] = {
|
|
deployedAddress: "",
|
|
emittedVAAs: []
|
|
};
|
|
}
|
|
|
|
if (!network) {
|
|
throw new Error("Network not defined in config file.");
|
|
}
|
|
|
|
if (process.argv[3] == "deploy") {
|
|
console.log(
|
|
`Deploying EVM network: ${process.argv[2]} to ${network.rpc}`
|
|
);
|
|
|
|
exec(
|
|
`cd chains/evm && forge install && forge build && forge create --legacy --rpc-url ${network.rpc} --private-key ${network.privateKey} src/Treasury.sol:Treasury && exit`,
|
|
(err, out, errStr) => {
|
|
if (err) {
|
|
throw new Error(err);
|
|
}
|
|
|
|
if (out) {
|
|
console.log(out);
|
|
deployment[[process.argv[2]]] = {
|
|
deployedAddress: "",
|
|
emittedVAAs: []
|
|
};
|
|
deployment[process.argv[2]].deployedAddress = out
|
|
.split("Deployed to: ")[1]
|
|
.split("\n")[0]
|
|
.trim();
|
|
deployment[process.argv[2]].emittedVAAs = [];
|
|
|
|
fs.writeFileSync(
|
|
"./deployment.json",
|
|
JSON.stringify(deployment, null, 4)
|
|
);
|
|
}
|
|
}
|
|
);
|
|
} else if (process.argv[3] == "register_chain") {
|
|
if (!deployment[process.argv[2]].deployedAddress) {
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const targetNetwork = config.networks[process.argv[4]];
|
|
const targetDeployment = deployment[process.argv[4]]
|
|
if (!targetDeployment.deployedAddress) {
|
|
throw new Error("Target Network not deployed yet!");
|
|
}
|
|
|
|
let emitterAddr = Buffer.from(
|
|
getEmitterAddressEth(targetDeployment.deployedAddress),
|
|
"hex"
|
|
);
|
|
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
|
|
const treasury = new ethers.Contract(
|
|
deployment[process.argv[2]].deployedAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/Treasury.sol/Treasury.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
await treasury.registerApplicationContracts(
|
|
targetNetwork.wormholeChainId,
|
|
emitterAddr
|
|
);
|
|
console.log(
|
|
`Network(${process.argv[2]}) Registered Emitter: ${targetDeployment.deployedAddress} from Chain: ${process.argv[4]}`
|
|
);
|
|
} else if (process.argv[3] == "get_tokens") {
|
|
if (!deployment[process.argv[2]].deployedAddress) {
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
const treasury = new ethers.Contract(
|
|
deployment[process.argv[2]].deployedAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/Treasury.sol/Treasury.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
|
|
const TKN = new ethers.Contract(
|
|
network.testToken,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/ERC20PresetMinterPauser.sol/ERC20PresetMinterPauser.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
console.log(`${process.argv[2]} Treasury has ${await treasury.getTKNCount()} tokens.`);
|
|
const tokenAmt = ethers.utils.parseUnits(process.argv[4], "18");
|
|
console.log(`Minting ${tokenAmt} tokens.`);
|
|
await TKN.mint(deployment[process.argv[2]].deployedAddress, tokenAmt, {
|
|
gasLimit: 2000000
|
|
});
|
|
await new Promise((r) => setTimeout(r, 3000));
|
|
console.log(`${process.argv[2]} Treasury has ${await treasury.getTKNCount()} tokens.`);
|
|
} else if (process.argv[3] == "attest_token") {
|
|
if (!deployment[process.argv[2]].deployedAddress) {
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
|
|
const networkTokenAttestation = await attestFromEth(
|
|
network.tokenBridgeAddress,
|
|
signer,
|
|
network.testToken
|
|
);
|
|
|
|
const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
|
|
const seq = parseSequenceFromLogEth(networkTokenAttestation, network.bridgeAddress);
|
|
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
|
|
console.log("Searching for: ", vaaURL);
|
|
let vaaBytes = await (await fetch(vaaURL)).json();
|
|
while(!vaaBytes.vaaBytes){
|
|
console.log("VAA not found, retrying in 5s!");
|
|
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
|
|
vaaBytes = await (await fetch(vaaURL)).json();
|
|
}
|
|
|
|
if (!deployment[process.argv[2]].emittedVAAs) {
|
|
deployment[process.argv[2]].emittedVAAs = [vaaBytes.vaaBytes];
|
|
} else {
|
|
deployment[process.argv[2]].emittedVAAs.push(vaaBytes.vaaBytes);
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
"./deployment.json",
|
|
JSON.stringify(deployment, null, 2)
|
|
);
|
|
console.log(
|
|
`Network(${process.argv[2]}) Emitted VAA: `,
|
|
vaaBytes.vaaBytes
|
|
);
|
|
|
|
// Now create the Wrapped Version of the Token on the target chain
|
|
const targetNetwork = config.networks[process.argv[4]];
|
|
const targetDeployment = deployment[process.argv[4]];
|
|
const targetSigner = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(targetNetwork.rpc)
|
|
);
|
|
const targetTokenBridge = new ethers.Contract(
|
|
targetNetwork.tokenBridgeAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/ITokenBridge.sol/ITokenBridge.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
targetSigner
|
|
);
|
|
|
|
await targetTokenBridge.createWrapped(Buffer.from(vaaBytes.vaaBytes, "base64"), {
|
|
gasLimit: 2000000
|
|
})
|
|
await new Promise((r) => setTimeout(r, 5000)); //Time out to let block propagate
|
|
const wrappedTokenAddress = await targetTokenBridge.wrappedAsset(network.wormholeChainId, Buffer.from(tryNativeToHexString(network.testToken, "ethereum"), "hex"));
|
|
console.log("Wrapped token created at: ", wrappedTokenAddress);
|
|
targetDeployment["wrappedTestTokenAddress"] = wrappedTokenAddress;
|
|
|
|
deployment[process.argv[4]] = targetDeployment;
|
|
fs.writeFileSync('./deployment.json', JSON.stringify(deployment, null, 4));
|
|
} else if (process.argv[3] == "get_token_counts") {
|
|
if (!deployment[process.argv[2]].deployedAddress) {
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
|
|
const treasury = new ethers.Contract(
|
|
deployment[process.argv[2]].deployedAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/Treasury.sol/Treasury.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
|
|
console.log(`${process.argv[2]} Treasury has ${await treasury.getTKNCount()} native TKN.`)
|
|
if(deployment[process.argv[2]]['wrappedTestTokenAddress']){
|
|
console.log(`${process.argv[2]} Treasury has ${await treasury.getWrappedCount(deployment[process.argv[2]]['wrappedTestTokenAddress'])} wrapped TKN.`)
|
|
}
|
|
} else if (process.argv[3] == "bridge_token") {
|
|
if (!deployment[process.argv[2]].deployedAddress) {
|
|
throw new Error("Deploy to this network first!");
|
|
}
|
|
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
|
|
const treasury = new ethers.Contract(
|
|
deployment[process.argv[2]].deployedAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/Treasury.sol/Treasury.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
|
|
//Multiply out the decimals to 18 (default for ERC20)
|
|
const bridgeAmt = ethers.utils.parseUnits(process.argv[5], "18");
|
|
|
|
|
|
// Remember to allow Token Bridge to move tokens from Treasury account to it's own account
|
|
console.log(`Approving ${process.argv[5]} (${bridgeAmt} in proper ERC20 format) Tokens to be bridged by Token Bridge`);
|
|
await treasury.approveTokenBridge(bridgeAmt, {
|
|
gasLimit: 2000000,
|
|
});
|
|
await new Promise((r) => setTimeout(r, 5000)); //Time out to let block propagate
|
|
|
|
const targetNetwork = config.networks[process.argv[4]];
|
|
const targetDeployment = deployment[process.argv[4]]
|
|
if (!targetDeployment.deployedAddress) {
|
|
throw new Error("Target Network not deployed yet!");
|
|
}
|
|
|
|
console.log(`Bridging ${process.argv[5]} Tokens which is ${bridgeAmt} Tokens`);
|
|
const targetRecepient = Buffer.from(tryNativeToHexString(targetDeployment.deployedAddress, "ethereum"), 'hex');
|
|
const tx = await (await treasury.bridgeToken(bridgeAmt, targetNetwork.wormholeChainId, targetRecepient)).wait();
|
|
const emitterAddr = getEmitterAddressEth(network.tokenBridgeAddress);
|
|
const seq = parseSequenceFromLogEth(tx, network.bridgeAddress);
|
|
const vaaURL = `${config.wormhole.restAddress}/v1/signed_vaa/${network.wormholeChainId}/${emitterAddr}/${seq}`;
|
|
let vaaBytes = await (await fetch(vaaURL)).json();
|
|
while(!vaaBytes.vaaBytes){
|
|
console.log("VAA not found, retrying in 5s!");
|
|
await new Promise((r) => setTimeout(r, 5000)); //Timeout to let Guardiand pick up log and have VAA ready
|
|
vaaBytes = await (await fetch(vaaURL)).json();
|
|
}
|
|
|
|
if (!deployment[process.argv[2]].emittedVAAs) {
|
|
deployment[process.argv[2]].emittedVAAs = [vaaBytes.vaaBytes];
|
|
} else {
|
|
deployment[process.argv[2]].emittedVAAs.push(vaaBytes.vaaBytes);
|
|
}
|
|
|
|
fs.writeFileSync(
|
|
"./deployment.json",
|
|
JSON.stringify(deployment, null, 2)
|
|
);
|
|
|
|
console.log(
|
|
`Network(${process.argv[2]}) Emitted VAA: `,
|
|
vaaBytes.vaaBytes
|
|
);
|
|
|
|
// Now create the Wrapped Version of the Token on the target chain
|
|
const targetSigner = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(targetNetwork.rpc)
|
|
);
|
|
const targetTokenBridge = new ethers.Contract(
|
|
targetNetwork.tokenBridgeAddress,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/ITokenBridge.sol/ITokenBridge.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
targetSigner
|
|
);
|
|
const completeTransferTx = await targetTokenBridge.completeTransfer(Buffer.from(vaaBytes.vaaBytes, "base64"));
|
|
console.log("Complete Transfer TX: ", await completeTransferTx.wait());
|
|
} else if (process.argv[3] == "debug") {
|
|
const signer = new ethers.Wallet(network.privateKey).connect(
|
|
new ethers.providers.JsonRpcProvider(network.rpc)
|
|
);
|
|
|
|
const TKN = new ethers.Contract(
|
|
network.testToken,
|
|
JSON.parse(
|
|
fs
|
|
.readFileSync(
|
|
"./chains/evm/out/ERC20PresetMinterPauser.sol/ERC20PresetMinterPauser.json"
|
|
)
|
|
.toString()
|
|
).abi,
|
|
signer
|
|
);
|
|
|
|
console.log((await TKN.allowance(deployment[process.argv[2]].deployedAddress, network.tokenBridgeAddress)).toNumber());
|
|
console.log((await TKN.balanceOf(network.tokenBridgeAddress)).toNumber());
|
|
} else {
|
|
throw new Error("Unkown command!");
|
|
}
|
|
}
|
|
main();
|