sdk/js: Near refactor, added integration tests (#1648)

* near-sdk-refactor: Fix attest
                 : Fix missing function calls
                 : make near on near
                 : renamed tryHexToNativeStringNear

* near-sdk-refactor: bumped near-sdk-js version

Co-authored-by: Josh Siegel <jsiegel@jumptrading.com>
This commit is contained in:
kev1n-peters 2022-09-28 08:53:15 -05:00 committed by GitHub
parent 02cf08531e
commit c5d2f9d54d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 1014 additions and 679 deletions

View File

@ -124,6 +124,7 @@ docker_build(
context = ".",
dockerfile = "Dockerfile.node",
target = "build",
ignore=["./sdk/js"]
)
def command_with_dlv(argv):
@ -697,9 +698,10 @@ if near:
)
docker_build(
ref = "near-contracts",
ref = "near-deploy",
context = "near",
dockerfile = "near/Dockerfile.contracts",
dockerfile = "near/Dockerfile.deploy",
ignore = ["./test"]
)
k8s_resource(

View File

@ -14,7 +14,7 @@ export async function execute_near(
let contracts = CONTRACTS[network]["near"];
let target_contract = "";
let numSubmits = 1
let numSubmits = 1;
switch (payload.module) {
case "Core":
@ -37,7 +37,7 @@ export async function execute_near(
if (contracts.nft_bridge === undefined) {
throw new Error("NFT bridge not supported yet for near");
}
numSubmits = 2
numSubmits = 2;
target_contract = contracts.nft_bridge;
switch (payload.type) {
case "ContractUpgrade":
@ -57,7 +57,7 @@ export async function execute_near(
if (contracts.token_bridge === undefined) {
throw new Error("Token bridge not supported yet for near");
}
numSubmits = 2
numSubmits = 2;
target_contract = contracts.token_bridge;
switch (payload.type) {
case "ContractUpgrade":
@ -89,9 +89,7 @@ export async function execute_near(
keyStore.setKey(n.networkId, n.deployerAccount, key);
let near = await nearAPI.connect({
deps: {
keyStore,
},
keyStore,
networkId: n.networkId,
nodeUrl: n.rpc,
});
@ -111,7 +109,7 @@ export async function execute_near(
if (numSubmits <= 1) {
console.log("Hash: " + result1.transaction.hash);
return
return;
}
// You have to feed a vaa twice into the contract (two submits),

View File

@ -216,6 +216,8 @@ const contract_registrations = {
process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA,
// TERRA
process.env.REGISTER_TERRA_TOKEN_BRIDGE_VAA,
// NEAR
process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA,
],
};

View File

@ -53,7 +53,7 @@ spec:
command:
- /bin/sh
- -c
- "npm run migrate && npx truffle exec scripts/deploy_test_token.js && npm run deploy-batched-vaa-sender && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_terra2_chain.js && npx truffle exec scripts/register_bsc_chain.js && npx truffle exec scripts/register_algo_chain.js && nc -lkp 2000 0.0.0.0"
- "npm run migrate && npx truffle exec scripts/deploy_test_token.js && npm run deploy-batched-vaa-sender && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_terra2_chain.js && npx truffle exec scripts/register_bsc_chain.js && npx truffle exec scripts/register_algo_chain.js && npx truffle exec scripts/register_near_chain.js && nc -lkp 2000 0.0.0.0"
readinessProbe:
periodSeconds: 1
failureThreshold: 300

View File

@ -55,7 +55,7 @@ spec:
command:
- /bin/sh
- -c
- "sed -i 's/CHAIN_ID=0x2/CHAIN_ID=0x4/g;s/EVM_CHAIN_ID=1/EVM_CHAIN_ID=1397/g' .env && npm run migrate && npx truffle exec scripts/deploy_test_token.js && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_terra2_chain.js && npx truffle exec scripts/register_eth_chain.js && npx truffle exec scripts/register_algo_chain.js && nc -lkp 2000 0.0.0.0"
- "sed -i 's/CHAIN_ID=0x2/CHAIN_ID=0x4/g;s/EVM_CHAIN_ID=1/EVM_CHAIN_ID=1397/g' .env && npm run migrate && npx truffle exec scripts/deploy_test_token.js && npx truffle exec scripts/register_solana_chain.js && npx truffle exec scripts/register_terra_chain.js && npx truffle exec scripts/register_terra2_chain.js && npx truffle exec scripts/register_eth_chain.js && npx truffle exec scripts/register_algo_chain.js && npx truffle exec scripts/register_near_chain.js && nc -lkp 2000 0.0.0.0"
readinessProbe:
periodSeconds: 1
failureThreshold: 300

View File

@ -46,12 +46,12 @@ spec:
readinessProbe:
tcpSocket:
port: 3030
- name: near-contracts
image: near-contracts
- name: near-deploy
image: near-deploy
command:
- /bin/sh
- -c
- "sh devnet_deploy.sh && touch success && sleep infinity"
- "sh /app/devnet_deploy.sh && touch success && sleep infinity"
readinessProbe:
tcpSocket:
port: 3030

View File

@ -0,0 +1,29 @@
// run this script with truffle exec
const jsonfile = require("jsonfile");
const TokenBridge = artifacts.require("TokenBridge");
const BridgeImplementationFullABI = jsonfile.readFileSync(
"../build/contracts/BridgeImplementation.json"
).abi;
const nearTokenBridgeVAA = process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA;
module.exports = async function (callback) {
try {
const accounts = await web3.eth.getAccounts();
const tokenBridge = new web3.eth.Contract(
BridgeImplementationFullABI,
TokenBridge.address
);
// Register the near token bridge endpoint
await tokenBridge.methods.registerChain("0x" + nearTokenBridgeVAA).send({
value: 0,
from: accounts[0],
gasLimit: 2000000,
});
callback();
} catch (e) {
callback(e);
}
};

28
near/Dockerfile.deploy Normal file
View File

@ -0,0 +1,28 @@
FROM debian@sha256:2ce44bbc00a79113c296d9d25524e15d423b23303fdbbe20190d2f96e0aeb251 as build
RUN apt-get update && apt-get install -y curl \
make \
build-essential
WORKDIR /app
COPY ./setup-rust.sh .
RUN ./setup-rust.sh
COPY . .
RUN ./build-contracts.sh
FROM node:16-alpine@sha256:004dbac84fed48e20f9888a23e32fa7cf83c2995e174a78d41d9a9dd1e051a20 AS deploy
WORKDIR /app
COPY package.json .
COPY package-lock.json .
COPY .env .
COPY devnet_deploy.sh .
COPY devnet_deploy.ts .
COPY --from=build /app/contracts/*/target/wasm32-unknown-unknown/release/*.wasm .
# mount the buildkit cache on npm's cache dir, install dependencies
RUN --mount=type=cache,target=/root/.npm npm ci --production

View File

@ -59,9 +59,7 @@ async function initNear() {
keyStore.setKey(config.networkId, config.masterAccount, masterKey);
let near = await nearAPI.connect({
deps: {
keyStore,
},
keyStore,
networkId: config.networkId,
nodeUrl: config.nodeUrl,
});
@ -76,21 +74,25 @@ async function initNear() {
);
if (e === "sandbox") {
let da = parseSeedPhrase("weather opinion slam purpose access artefact word orbit matter rice poem badge");
console.log(da);
let resp = await masterAccount.createAccount(
"devnet.test.near",
da.publicKey,
new BN(10).pow(new BN(27))
);
let da = parseSeedPhrase(
"weather opinion slam purpose access artefact word orbit matter rice poem badge"
);
console.log(da);
let resp = await masterAccount.createAccount(
"devnet.test.near",
da.publicKey,
new BN(10).pow(new BN(27))
);
console.log("devnet.test.near funded");
console.log("devnet.test.near funded");
}
const wormholeContract = await fs.readFileSync("./near_wormhole.wasm");
const tokenContract = await fs.readFileSync("./near_token_bridge.wasm");
const nftContract = await fs.readFileSync("./near_nft_bridge.wasm");
const testContract = await fs.readFileSync("./near_mock_bridge_integration.wasm");
const testContract = await fs.readFileSync(
"./near_mock_bridge_integration.wasm"
);
let wormholeAccount: any;
@ -108,27 +110,27 @@ async function initNear() {
new BN("20000000000000000000000000")
);
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: {emitter: config.tokenAccount},
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
})
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: {emitter: config.testAccount},
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
})
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: {emitter: config.nftAccount},
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
})
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: { emitter: config.tokenAccount },
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
});
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: { emitter: config.testAccount },
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
});
await wormholeAccount.functionCall({
contractId: config.wormholeAccount,
methodName: "register_emitter",
args: { emitter: config.nftAccount },
attachedDeposit: new BN("30000000000000000000000"),
gas: new BN("100000000000000"),
});
} else {
// This uses the standard API to redeploy ... we can migrate over to the vaa's later
console.log(
@ -140,18 +142,17 @@ async function initNear() {
);
await wormholeAccount.deployContract(wormholeContract);
// console.log("migrating " + config.wormholeAccount);
// console.log(
// await wormholeAccount.functionCall({
// contractId: config.wormholeAccount,
// methodName: "migrate",
// args: {},
// attachedDeposit: new BN(1),
// gas: new BN("100000000000000"),
// })
// );
// console.log("done migrating " + config.tokenAccount);
// console.log("migrating " + config.wormholeAccount);
// console.log(
// await wormholeAccount.functionCall({
// contractId: config.wormholeAccount,
// methodName: "migrate",
// args: {},
// attachedDeposit: new BN(1),
// gas: new BN("100000000000000"),
// })
// );
// console.log("done migrating " + config.tokenAccount);
}
let tokenAccount: any;
@ -170,19 +171,21 @@ async function initNear() {
tokenAccount = new nearAPI.Account(near.connection, config.tokenAccount);
await tokenAccount.deployContract(tokenContract);
console.log( await tokenAccount.viewFunction(config.tokenAccount, "emitter", {}));
console.log(
await tokenAccount.viewFunction(config.tokenAccount, "emitter", {})
);
// console.log("migrating " + config.tokenAccount);
// console.log(
// await tokenAccount.functionCall({
// contractId: config.tokenAccount,
// methodName: "migrate",
// args: {},
// attachedDeposit: new BN(1),
// gas: new BN("100000000000000"),
// })
// );
// console.log("done migrating " + config.tokenAccount);
// console.log("migrating " + config.tokenAccount);
// console.log(
// await tokenAccount.functionCall({
// contractId: config.tokenAccount,
// methodName: "migrate",
// args: {},
// attachedDeposit: new BN(1),
// gas: new BN("100000000000000"),
// })
// );
// console.log("done migrating " + config.tokenAccount);
}
let nftAccount: any;
@ -286,33 +289,33 @@ async function initNear() {
});
}
// for (const line of vaasNFT) {
// console.log("Submitting to " + config.nftAccount + ": " + line);
//
// try {
// await masterAccount.functionCall({
// contractId: config.nftAccount,
// methodName: "submit_vaa",
// args: {
// vaa: line,
// },
// attachedDeposit: new BN("30000000000000000000000"),
// gas: new BN("300000000000000"),
// });
//
// await masterAccount.functionCall({
// contractId: config.nftAccount,
// methodName: "submit_vaa",
// args: {
// vaa: line,
// },
// attachedDeposit: new BN("30000000000000000000000"),
// gas: new BN("300000000000000"),
// });
// } catch {
// console.log("Exception thrown.. ");
// }
// }
// for (const line of vaasNFT) {
// console.log("Submitting to " + config.nftAccount + ": " + line);
//
// try {
// await masterAccount.functionCall({
// contractId: config.nftAccount,
// methodName: "submit_vaa",
// args: {
// vaa: line,
// },
// attachedDeposit: new BN("30000000000000000000000"),
// gas: new BN("300000000000000"),
// });
//
// await masterAccount.functionCall({
// contractId: config.nftAccount,
// methodName: "submit_vaa",
// args: {
// vaa: line,
// },
// attachedDeposit: new BN("30000000000000000000000"),
// gas: new BN("300000000000000"),
// });
// } catch {
// console.log("Exception thrown.. ");
// }
// }
console.log("nft bridge booted");

View File

@ -51,9 +51,7 @@ async function initNear() {
keyStore.setKey(config.networkId, config.portalAccount, portalMasterKey);
let near = await nearAPI.connect({
deps: {
keyStore,
},
keyStore,
networkId: config.networkId,
nodeUrl: config.nodeUrl,
});

36
near/package-lock.json generated
View File

@ -18,6 +18,7 @@
"env-cmd": "^10.1.0",
"ethers": "^5.6.5",
"near-api-js": "^0.43.1",
"near-seed-phrase": "^0.2.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -35,7 +36,6 @@
"@types/prompt": "^1.1.2",
"cssnano": "^5.0.7",
"gh-pages": "^3.1.0",
"near-seed-phrase": "^0.2.0",
"parcel-bundler": "^1.12.5",
"postcss": "^8.3.6",
"prompt": "^1.3.0",
@ -44,11 +44,12 @@
},
"../sdk/js": {
"name": "@certusone/wormhole-sdk",
"version": "0.6.2",
"version": "0.6.3",
"license": "Apache-2.0",
"dependencies": {
"@certusone/wormhole-sdk-proto-web": "^0.0.3",
"@certusone/wormhole-sdk-proto-web": "^0.0.5",
"@certusone/wormhole-sdk-wasm": "^0.0.1",
"@injectivelabs/sdk-ts": "^1.0.75",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^3.1.3",
@ -56,10 +57,12 @@
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
"near-api-js": "^0.45.1"
"near-api-js": "^0.44.2"
},
"devDependencies": {
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@injectivelabs/networks": "^1.0.12",
"@injectivelabs/tx-ts": "^1.0.22",
"@openzeppelin/contracts": "^4.2.0",
"@typechain/ethers-v5": "^7.0.1",
"@types/jest": "^27.0.2",
@ -3319,7 +3322,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz",
"integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==",
"dev": true,
"dependencies": {
"@types/node": "11.11.6",
"create-hash": "^1.1.0",
@ -3331,7 +3333,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/bip39-light/-/bip39-light-1.0.7.tgz",
"integrity": "sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q==",
"dev": true,
"dependencies": {
"create-hash": "^1.1.0",
"pbkdf2": "^3.0.9"
@ -3340,8 +3341,7 @@
"node_modules/bip39/node_modules/@types/node": {
"version": "11.11.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
"integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==",
"dev": true
"integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ=="
},
"node_modules/blakejs": {
"version": "1.2.1",
@ -9180,7 +9180,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/near-hd-key/-/near-hd-key-1.2.1.tgz",
"integrity": "sha512-SIrthcL5Wc0sps+2e1xGj3zceEa68TgNZDLuCx0daxmfTP7sFTB3/mtE2pYhlFsCxWoMn+JfID5E1NlzvvbRJg==",
"dev": true,
"dependencies": {
"bip39": "3.0.2",
"create-hmac": "1.1.7",
@ -9191,7 +9190,6 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz",
"integrity": "sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ==",
"dev": true,
"dependencies": {
"bip39-light": "^1.0.7",
"bs58": "^4.0.1",
@ -9203,7 +9201,6 @@
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
"dev": true,
"dependencies": {
"safe-buffer": "^5.0.1"
}
@ -9212,7 +9209,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
"integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
"dev": true,
"dependencies": {
"base-x": "^3.0.2"
}
@ -16625,9 +16621,12 @@
"@certusone/wormhole-sdk": {
"version": "file:../sdk/js",
"requires": {
"@certusone/wormhole-sdk-proto-web": "^0.0.3",
"@certusone/wormhole-sdk-proto-web": "^0.0.5",
"@certusone/wormhole-sdk-wasm": "^0.0.1",
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@injectivelabs/networks": "^1.0.12",
"@injectivelabs/sdk-ts": "^1.0.75",
"@injectivelabs/tx-ts": "^1.0.22",
"@openzeppelin/contracts": "^4.2.0",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
@ -16644,7 +16643,7 @@
"ethers": "^5.6.8",
"jest": "^27.3.1",
"js-base64": "^3.6.1",
"near-api-js": "^0.45.1",
"near-api-js": "^0.44.2",
"prettier": "^2.3.2",
"ts-jest": "^27.0.7",
"tslint": "^6.1.3",
@ -17742,7 +17741,6 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz",
"integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==",
"dev": true,
"requires": {
"@types/node": "11.11.6",
"create-hash": "^1.1.0",
@ -17753,8 +17751,7 @@
"@types/node": {
"version": "11.11.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
"integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==",
"dev": true
"integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ=="
}
}
},
@ -17762,7 +17759,6 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/bip39-light/-/bip39-light-1.0.7.tgz",
"integrity": "sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q==",
"dev": true,
"requires": {
"create-hash": "^1.1.0",
"pbkdf2": "^3.0.9"
@ -22478,7 +22474,6 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/near-hd-key/-/near-hd-key-1.2.1.tgz",
"integrity": "sha512-SIrthcL5Wc0sps+2e1xGj3zceEa68TgNZDLuCx0daxmfTP7sFTB3/mtE2pYhlFsCxWoMn+JfID5E1NlzvvbRJg==",
"dev": true,
"requires": {
"bip39": "3.0.2",
"create-hmac": "1.1.7",
@ -22489,7 +22484,6 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz",
"integrity": "sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ==",
"dev": true,
"requires": {
"bip39-light": "^1.0.7",
"bs58": "^4.0.1",
@ -22501,7 +22495,6 @@
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.0.1"
}
@ -22510,7 +22503,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
"integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
"dev": true,
"requires": {
"base-x": "^3.0.2"
}

View File

@ -21,6 +21,7 @@
"env-cmd": "^10.1.0",
"ethers": "^5.6.5",
"near-api-js": "^0.43.1",
"near-seed-phrase": "^0.2.0",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
@ -38,7 +39,6 @@
"@types/prompt": "^1.1.2",
"cssnano": "^5.0.7",
"gh-pages": "^3.1.0",
"near-seed-phrase": "^0.2.0",
"parcel-bundler": "^1.12.5",
"postcss": "^8.3.6",
"prompt": "^1.3.0",

View File

@ -18,14 +18,13 @@ import {
} from "@certusone/wormhole-sdk/node_modules/near-api-js";
import {
CHAIN_ID_ETH,
CHAIN_ID_NEAR,
CONTRACTS,
attestTokenFromNear,
getSignedVAAWithRetry,
hexToUint8Array,
transferTokenFromNear,
uint8ArrayToHex,
parseSequenceFromLogNear,
getEmitterAddressNear,
} from "@certusone/wormhole-sdk";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
// @ts-ignore
@ -75,17 +74,12 @@ async function transferTest() {
// connect to near...
let near = await nearConnect({
headers: {},
deps: {
keyStore,
},
keyStore,
networkId: networkId as string,
nodeUrl: nearNodeUrl as string,
});
console.log(
"Attesting",
NEAR_TOKEN_ADDRESS,
);
console.log("Attesting", NEAR_TOKEN_ADDRESS);
prompt.message = "";
const { input } = await prompt.get({
@ -104,20 +98,30 @@ async function transferTest() {
near.connection,
process.env.NEAR_ACCOUNT as string
);
const provider = near.connection.provider;
let [sequence, emitterAddress] = await attestTokenFromNear(
userAccount,
const attestMsgs = await attestTokenFromNear(
provider,
CONTRACTS.MAINNET.near.core,
CONTRACTS.MAINNET.near.token_bridge,
NEAR_TOKEN_ADDRESS
);
if (sequence === -1) {
let attestOutcome;
for (const msg of attestMsgs) {
attestOutcome = await userAccount.functionCall(msg);
}
const sequence = parseSequenceFromLogNear(attestOutcome);
if (sequence === null) {
console.log("No sequence found, check above for error");
process.exit(1);
}
console.log("emitterAddress:", emitterAddress, "sequence:", sequence);
console.log(
"emitterAddress:",
getEmitterAddressNear(CONTRACTS.MAINNET.near.token_bridge),
"sequence:",
sequence
);
console.log(
`If this script hangs, try https://wormhole-v2-mainnet-api.certus.one/v1/signed_vaa/15/${emitterAddress}/${sequence.toString()}`
@ -126,8 +130,8 @@ async function transferTest() {
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
WORMHOLE_RPC_HOSTS,
CHAIN_ID_NEAR,
emitterAddress,
sequence.toString(),
getEmitterAddressNear(CONTRACTS.MAINNET.near.token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}

View File

@ -81,9 +81,7 @@ async function transferTest() {
// connect to near...
let near = await nearConnect({
headers: {},
deps: {
keyStore,
},
keyStore,
networkId: networkId as string,
nodeUrl: nearNodeUrl as string,
});
@ -129,8 +127,8 @@ async function transferTest() {
console.log("account already registered");
}
// like this
await redeemOnNear(userAccount, token_bridge, hexToUint8Array(trans))
// like this
await redeemOnNear(userAccount, token_bridge, hexToUint8Array(trans));
}
transferTest();

View File

@ -1,46 +1,16 @@
const nearAPI = require("near-api-js");
const BN = require("bn.js");
const fs = require("fs");
const fetch = require("node-fetch");
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
import {
CONTRACTS,
attestNearFromNear,
attestTokenFromNear,
attestFromAlgorand,
createWrappedOnAlgorand,
tryNativeToUint8Array,
createWrappedOnNear,
getEmitterAddressAlgorand,
getForeignAssetAlgorand,
getForeignAssetNear,
getIsTransferCompletedNear,
getIsWrappedAssetNear,
getOriginalAssetNear,
getSignedVAAWithRetry,
redeemOnAlgorand,
redeemOnNear,
transferFromAlgorand,
transferNearFromNear,
transferTokenFromNear,
parseSequenceFromLogNear,
getEmitterAddressNear,
} from "@certusone/wormhole-sdk";
const wh = require("@certusone/wormhole-sdk");
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_NEAR,
ChainId,
ChainName,
textToHexString,
textToUint8Array,
} from "@certusone/wormhole-sdk/lib/cjs/utils";
import {
_parseVAAAlgorand,
} from "@certusone/wormhole-sdk/lib/cjs/algorand";
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/cjs/algorand";
function getConfig(env: any) {
switch (env) {
@ -77,7 +47,6 @@ async function initNear() {
let masterKey = nearAPI.utils.KeyPair.fromString(
"ed25519:5dJ7Nsq4DQBdiGvZLPyjRVmhtRaScahsREpEPtaAyE9Z3CgyZFsaBwpybCRBMugiwhbFCUkqHk7PJ3BVcgZZ9Lgk"
);
let masterPubKey = masterKey.getPublicKey();
let keyStore = new nearAPI.keyStores.InMemoryKeyStore();
keyStore.setKey(config.networkId, config.masterAccount, masterKey);
@ -93,6 +62,7 @@ async function initNear() {
near.connection,
config.masterAccount
);
const provider = near.connection.provider;
console.log(
"Finish init NEAR masterAccount: " +
@ -114,13 +84,22 @@ async function initNear() {
);
console.log("attesting Near itself");
let s = await attestNearFromNear(masterAccount, core_bridge, token_bridge);
const attestMsg = await attestNearFromNear(
provider,
core_bridge,
token_bridge
);
const attestOutcome = await masterAccount.functionCall(attestMsg);
const attestSeq = parseSequenceFromLogNear(attestOutcome);
if (attestSeq === null) {
throw new Error("attestSeq is null");
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["https://wormhole-v2-testnet-api.certus.one"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
"near",
getEmitterAddressNear(token_bridge),
attestSeq,
{
transport: NodeHttpTransport(),
}
@ -128,21 +107,29 @@ async function initNear() {
console.log("vaa: " + Buffer.from(signedVAA).toString("hex"));
s = await transferNearFromNear(
masterAccount,
const transferMsg = await transferNearFromNear(
provider,
core_bridge,
token_bridge,
BigInt(1000000000000000000000000),
tryNativeToUint8Array("0x3bC7f2e458aC4E55F941C458cfD8c6851a591B4F", "ethereum"),
tryNativeToUint8Array(
"0x3bC7f2e458aC4E55F941C458cfD8c6851a591B4F",
"ethereum"
),
2,
BigInt(0)
);
const transferOutcome = await masterAccount.functionCall(transferMsg);
const transferSeq = parseSequenceFromLogNear(transferOutcome);
if (transferSeq === null) {
throw new Error("transferSeq is null");
}
const { vaaBytes: signedTrans } = await getSignedVAAWithRetry(
["https://wormhole-v2-testnet-api.certus.one"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
"near",
getEmitterAddressNear(token_bridge),
transferSeq,
{
transport: NodeHttpTransport(),
}
@ -150,8 +137,8 @@ async function initNear() {
console.log("vaa: " + Buffer.from(signedTrans).toString("hex"));
let p = _parseVAAAlgorand(signedTrans);
console.log(p);
let p = _parseVAAAlgorand(signedTrans);
console.log(p);
}
initNear();

View File

@ -1,39 +1,20 @@
// npx pretty-quick
const sha256 = require("js-sha256");
const nearAPI = require("near-api-js");
const fs = require("fs").promises;
const assert = require("assert").strict;
const fetch = require("node-fetch");
const elliptic = require("elliptic");
const web3Utils = require("web3-utils");
import { zeroPad } from "@ethersproject/bytes";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
import { Account as nearAccount } from "@certusone/wormhole-sdk/node_modules/near-api-js";
const BN = require("bn.js");
import { TestLib } from "./testlib";
import algosdk, {
Account,
Algodv2,
OnApplicationComplete,
SuggestedParams,
bigIntToBytes,
decodeAddress,
getApplicationAddress,
makeApplicationCallTxnFromObject,
makePaymentTxnWithSuggestedParamsFromObject,
waitForConfirmation,
} from "@certusone/wormhole-sdk/node_modules/algosdk";
import {
createAsset,
getAlgoClient,
getBalance,
getBalances,
getForeignAssetFromVaaAlgorand,
getTempAccounts,
signSendAndConfirmAlgorand,
} from "./algoHelpers";
@ -42,44 +23,28 @@ import {
CHAIN_ID_ALGORAND,
CHAIN_ID_NEAR,
ChainId,
ChainName,
textToHexString,
textToUint8Array,
} from "@certusone/wormhole-sdk/lib/cjs/utils";
import { safeBigIntToNumber } from "@certusone/wormhole-sdk/lib/cjs/utils/bigint";
import {
CONTRACTS,
attestNearFromNear,
attestTokenFromNear,
attestFromAlgorand,
createWrappedOnAlgorand,
createWrappedOnNear,
getEmitterAddressAlgorand,
getForeignAssetAlgorand,
getForeignAssetNear,
getIsTransferCompletedNear,
getIsWrappedAssetNear,
getOriginalAssetNear,
getSignedVAAWithRetry,
redeemOnAlgorand,
redeemOnNear,
transferFromAlgorand,
transferNearFromNear,
transferTokenFromNear,
} from "@certusone/wormhole-sdk";
const wh = require("@certusone/wormhole-sdk");
import { parseSequenceFromLogAlgorand } from "@certusone/wormhole-sdk/lib/cjs/bridge";
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/cjs/algorand";
import {
getMessageFee,
optin,
TransactionSignerPair,
_parseVAAAlgorand,
} from "@certusone/wormhole-sdk/lib/cjs/algorand";
getEmitterAddressNear,
parseSequenceFromLogNear,
} from "@certusone/wormhole-sdk/src";
export const uint8ArrayToHex = (a: Uint8Array): string =>
Buffer.from(a).toString("hex");
@ -131,21 +96,6 @@ export function logNearGas(result: any, comment: string) {
);
}
export function parseSequenceFromLogNear(result: any): [number, string] {
let sequence = "";
for (const o of result.receipts_outcome) {
for (const l of o.outcome.logs) {
if (l.startsWith("EVENT_JSON:")) {
const body = JSON.parse(l.slice(11));
if (body.standard == "wormhole" && body.event == "publish") {
return [body.seq, body.emitter];
}
}
}
}
return [-1, ""];
}
async function testNearSDK() {
let config = getConfig(process.env.NEAR_ENV || "sandbox");
@ -157,7 +107,6 @@ async function testNearSDK() {
let masterKey = nearAPI.utils.KeyPair.fromString(
keyFile.secret_key || keyFile.private_key
);
let masterPubKey = masterKey.getPublicKey();
let keyStore = new nearAPI.keyStores.InMemoryKeyStore();
keyStore.setKey(config.networkId, config.masterAccount, masterKey);
@ -197,6 +146,7 @@ async function testNearSDK() {
new BN(10).pow(new BN(27))
);
const userAccount = new nearAPI.Account(near.connection, config.userAccount);
const provider = near.connection.provider;
console.log(
"creating a second user account: " +
@ -223,13 +173,8 @@ async function testNearSDK() {
let algoCore = BigInt(CONTRACTS.DEVNET.algorand.core);
let algoToken = BigInt(CONTRACTS.DEVNET.algorand.token_bridge);
const tbAddr: string = getApplicationAddress(algoToken);
const decTbAddr: Uint8Array = decodeAddress(tbAddr).publicKey;
const aa: string = uint8ArrayToHex(decTbAddr);
const algoClient: algosdk.Algodv2 = getAlgoClient();
const tempAccts: Account[] = await getTempAccounts();
const numAccts: number = tempAccts.length;
const algoWallet: Account = tempAccts[0];
@ -253,9 +198,16 @@ async function testNearSDK() {
seq = seq + 1;
let usdcp = _parseVAAAlgorand(usdcvaa);
let usdc = await createWrappedOnNear(userAccount, token_bridge, usdcvaa);
const createWrappedMsgs = await createWrappedOnNear(
provider,
token_bridge,
usdcvaa
);
let usdc;
for (const msg of createWrappedMsgs) {
const tx = await userAccount.functionCall(msg);
usdc = nearAPI.providers.getTransactionLastResult(tx);
}
console.log(usdc);
console.log("Creating USDC token on algorand");
@ -298,7 +250,12 @@ async function testNearSDK() {
console.log(trans);
console.log(
await redeemOnNear(userAccount, token_bridge, hexToUint8Array(trans))
await redeemOnNear(
provider,
userAccount.account_id,
token_bridge,
hexToUint8Array(trans)
)
);
}
console.log(".. created some USDC on near");
@ -306,8 +263,9 @@ async function testNearSDK() {
let wrappedTransfer;
{
console.log("transfer USDC from near to algorand");
let s = await transferTokenFromNear(
userAccount,
const transferMsg = await transferTokenFromNear(
provider,
userAccount.account_id,
core_bridge,
token_bridge,
usdc,
@ -316,12 +274,18 @@ async function testNearSDK() {
8,
BigInt(0)
);
const transferOutcome = await userAccount.functionCall(transferMsg);
const sequence = parseSequenceFromLogNear(transferOutcome);
if (sequence === null) {
console.log("sequence is null");
return;
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -407,7 +371,12 @@ async function testNearSDK() {
console.log("redeeming P3 NEAR on Near");
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearP3)
await redeemOnNear(
provider,
user2Account.account_id,
token_bridge,
transferAlgoToNearP3
)
);
console.log("transfering USDC from Algo To Near... getting the vaa");

View File

@ -1,12 +1,6 @@
// npx pretty-quick
const sha256 = require("js-sha256");
const fs = require("fs").promises;
const assert = require("assert").strict;
const fetch = require("node-fetch");
const elliptic = require("elliptic");
const web3Utils = require("web3-utils");
import { zeroPad } from "@ethersproject/bytes";
import { NodeHttpTransport } from "@improbable-eng/grpc-web-node-http-transport";
import {
@ -23,22 +17,12 @@ import { TestLib } from "./testlib";
import algosdk, {
Account,
Algodv2,
OnApplicationComplete,
SuggestedParams,
bigIntToBytes,
decodeAddress,
getApplicationAddress,
makeApplicationCallTxnFromObject,
makePaymentTxnWithSuggestedParamsFromObject,
waitForConfirmation,
} from "@certusone/wormhole-sdk/node_modules/algosdk";
import {
getAlgoClient,
getBalance,
getBalances,
getForeignAssetFromVaaAlgorand,
getTempAccounts,
signSendAndConfirmAlgorand,
} from "./algoHelpers";
@ -47,13 +31,8 @@ import {
CHAIN_ID_ALGORAND,
CHAIN_ID_NEAR,
ChainId,
ChainName,
textToHexString,
textToUint8Array,
} from "@certusone/wormhole-sdk/lib/cjs/utils";
import { safeBigIntToNumber } from "@certusone/wormhole-sdk/lib/cjs/utils/bigint";
import {
CONTRACTS,
attestNearFromNear,
@ -66,8 +45,6 @@ import {
getForeignAssetAlgorand,
getForeignAssetNear,
getIsTransferCompletedNear,
getIsWrappedAssetNear,
getOriginalAssetNear,
getSignedVAAWithRetry,
redeemOnAlgorand,
redeemOnNear,
@ -76,19 +53,10 @@ import {
transferTokenFromNear,
} from "@certusone/wormhole-sdk";
const wh = require("@certusone/wormhole-sdk");
import { parseSequenceFromLogAlgorand } from "@certusone/wormhole-sdk/lib/cjs/bridge";
import {
parseSequenceFromLogAlgorand,
parseSequenceFromLogNear,
} from "@certusone/wormhole-sdk/lib/cjs/bridge";
import {
getMessageFee,
optin,
TransactionSignerPair,
_parseVAAAlgorand,
} from "@certusone/wormhole-sdk/lib/cjs/algorand";
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/cjs/algorand";
import { parseSequenceFromLogNear } from "@certusone/wormhole-sdk/src";
export const uint8ArrayToHex = (a: Uint8Array): string =>
Buffer.from(a).toString("hex");
@ -218,7 +186,6 @@ async function testNearSDK() {
let masterKey = nearUtils.KeyPair.fromString(
keyFile.secret_key || keyFile.private_key
);
let masterPubKey = masterKey.getPublicKey();
let keyStore = new nearKeyStores.InMemoryKeyStore();
keyStore.setKey(
@ -229,9 +196,7 @@ async function testNearSDK() {
let near = await nearConnect({
headers: {},
deps: {
keyStore,
},
keyStore,
networkId: config.networkId as string,
nodeUrl: config.nodeUrl as string,
});
@ -274,6 +239,7 @@ async function testNearSDK() {
near.connection,
config.userAccount as string
);
const provider = near.connection.provider;
console.log(
"creating a second user account: " +
@ -317,11 +283,9 @@ async function testNearSDK() {
const tbAddr: string = getApplicationAddress(algoToken);
const decTbAddr: Uint8Array = decodeAddress(tbAddr).publicKey;
const aa: string = uint8ArrayToHex(decTbAddr);
const algoClient: algosdk.Algodv2 = getAlgoClient();
const tempAccts: Account[] = await getTempAccounts();
const numAccts: number = tempAccts.length;
const algoWallet: Account = tempAccts[0];
@ -353,12 +317,15 @@ async function testNearSDK() {
{ transport: NodeHttpTransport() }
);
let noriumNear = await createWrappedOnNear(
userAccount,
for (const msg of await createWrappedOnNear(
provider,
token_bridge,
vaaBytes
);
console.log("for norium, createWrappedOnNear returned " + noriumNear);
)) {
await userAccount.functionCall(msg);
}
console.log("for norium, createWrappedOnNear returned");
let account_hash = await userAccount.viewFunction(
token_bridge,
@ -455,14 +422,22 @@ async function testNearSDK() {
console.log("calling createWrappedOnNear to create usdc");
if (
(await getIsTransferCompletedNear(userAccount, token_bridge, usdcvaa)) ==
true
(await getIsTransferCompletedNear(provider, token_bridge, usdcvaa)) == true
) {
console.log("getIsTransferCompleted returned incorrect value (true)");
process.exit(1);
}
let usdc = await createWrappedOnNear(userAccount, token_bridge, usdcvaa);
const createWrappedMsgs = await createWrappedOnNear(
provider,
token_bridge,
usdcvaa
);
let usdc;
for (const msg of createWrappedMsgs) {
const tx = await userAccount.functionCall(msg);
usdc = nearProviders.getTransactionLastResult(tx);
}
console.log("createWrappedOnNear returned " + usdc);
if (usdc === "") {
@ -471,15 +446,14 @@ async function testNearSDK() {
}
if (
(await getIsTransferCompletedNear(userAccount, token_bridge, usdcvaa)) ==
false
(await getIsTransferCompletedNear(provider, token_bridge, usdcvaa)) == false
) {
console.log("getIsTransferCompleted returned incorrect value (false)");
process.exit(1);
}
let aname = await getForeignAssetNear(
userAccount,
provider,
token_bridge,
usdcp.FromChain as ChainId,
usdcp.Contract as string
@ -518,9 +492,15 @@ async function testNearSDK() {
console.log(trans);
try {
console.log(
await redeemOnNear(userAccount, token_bridge, hexToUint8Array(trans))
const redeemMsgs = await redeemOnNear(
provider,
userAccount.accountId,
token_bridge,
hexToUint8Array(trans)
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
console.log("This should have thrown a exception..");
process.exit(1);
} catch (error) {
@ -541,30 +521,52 @@ async function testNearSDK() {
);
console.log("myAddress: " + myAddress2);
console.log(
await redeemOnNear(userAccount, token_bridge, hexToUint8Array(trans))
const redeemMsgs = await redeemOnNear(
provider,
userAccount.accountId,
token_bridge,
hexToUint8Array(trans)
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
}
console.log(".. created some USDC");
console.log("Redeeming norium on near");
await redeemOnNear(userAccount, token_bridge, signedVaa.vaaBytes);
for (const msg of await redeemOnNear(
provider,
userAccount.accountId,
token_bridge,
signedVaa.vaaBytes
)) {
await userAccount.functionCall(msg);
}
let nativeAttest;
{
console.log("attesting: " + randoToken);
let s = await attestTokenFromNear(
userAccount,
const attestMsgs = await attestTokenFromNear(
provider,
core_bridge,
token_bridge,
randoToken
);
console.log(s);
let sequence;
for (const msg of attestMsgs) {
const tx = await userAccount.functionCall(msg);
sequence = parseSequenceFromLogNear(tx);
}
if (!sequence) {
console.log("sequence is null");
process.exit(1);
}
console.log(getEmitterAddressNear(token_bridge));
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -576,7 +578,7 @@ async function testNearSDK() {
console.log(p.FromChain as ChainId, p.Contract as string);
let a = await getForeignAssetNear(
userAccount,
provider,
token_bridge,
p.FromChain as ChainId,
p.Contract as string
@ -592,13 +594,22 @@ async function testNearSDK() {
let nearAttest;
{
console.log("attesting Near itself");
let s = await attestNearFromNear(userAccount, core_bridge, token_bridge);
const attestMsg = await attestNearFromNear(
provider,
core_bridge,
token_bridge
);
const tx = await userAccount.functionCall(attestMsg);
const sequence = parseSequenceFromLogNear(tx);
if (sequence === null) {
console.log("sequence is null");
process.exit(1);
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -607,7 +618,7 @@ async function testNearSDK() {
console.log("vaa: " + Buffer.from(signedVAA).toString("hex"));
let p = _parseVAAAlgorand(signedVAA);
let a = await getForeignAssetNear(
userAccount,
provider,
token_bridge,
p.FromChain as ChainId,
p.Contract as string
@ -658,8 +669,9 @@ async function testNearSDK() {
usdc
);
let s = await transferTokenFromNear(
userAccount,
const transferMsgs = await transferTokenFromNear(
provider,
userAccount.accountId,
core_bridge,
token_bridge,
usdc,
@ -668,12 +680,21 @@ async function testNearSDK() {
8,
BigInt(0)
);
let sequence;
for (const msg of transferMsgs) {
const tx = await userAccount.functionCall(msg);
sequence = parseSequenceFromLogNear(tx);
}
if (!sequence) {
console.log("sequence is null");
process.exit(1);
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -698,8 +719,9 @@ async function testNearSDK() {
let randoTransfer;
{
console.log("YYY transfer rando token from near to algorand");
let s = await transferTokenFromNear(
userAccount,
const transferMsgs = await transferTokenFromNear(
provider,
userAccount.accountId,
core_bridge,
token_bridge,
randoToken,
@ -708,12 +730,21 @@ async function testNearSDK() {
8,
BigInt(0)
);
let sequence;
for (const msg of transferMsgs) {
const tx = await userAccount.functionCall(msg);
sequence = parseSequenceFromLogNear(tx);
}
if (!sequence) {
console.log("sequence is null");
process.exit(1);
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -723,16 +754,6 @@ async function testNearSDK() {
randoTransfer = signedVAA;
console.log(_parseVAAAlgorand(randoTransfer));
if (s[1] != getEmitterAddressNear(token_bridge)) {
console.log(
"Unexpected emitter address: " +
s[1] +
"!=" +
getEmitterAddressNear(token_bridge)
);
process.exit(1);
}
}
console.log(
@ -750,8 +771,8 @@ async function testNearSDK() {
let nearTransfer;
{
console.log("transfer near from near to algorand");
let s = await transferNearFromNear(
userAccount,
const transferMsg = await transferNearFromNear(
provider,
core_bridge,
token_bridge,
BigInt(1000000000000000000000000),
@ -759,12 +780,18 @@ async function testNearSDK() {
8,
BigInt(0)
);
const tx = await userAccount.functionCall(transferMsg);
const sequence = parseSequenceFromLogNear(tx);
if (sequence === null) {
console.log("sequence is null");
process.exit(1);
}
const { vaaBytes: signedVAA } = await getSignedVAAWithRetry(
["http://localhost:7071"],
CHAIN_ID_NEAR,
s[1],
s[0].toString(),
getEmitterAddressNear(token_bridge),
sequence,
{
transport: NodeHttpTransport(),
}
@ -944,9 +971,15 @@ async function testNearSDK() {
}
console.log("redeeming USDC on Near");
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearUSDC)
let redeemMsgs = await redeemOnNear(
provider,
user2Account.accountId,
token_bridge,
transferAlgoToNearUSDC
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
console.log(
"YYY redeeming Rando on Near: " + uint8ArrayToHex(transferAlgoToNearRando)
@ -964,9 +997,15 @@ async function testNearSDK() {
})
);
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearRando)
redeemMsgs = await redeemOnNear(
provider,
user2Account.accountId,
token_bridge,
transferAlgoToNearRando
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
console.log(
await userAccount.viewFunction(randoToken, "ft_balance_of", {
@ -981,9 +1020,16 @@ async function testNearSDK() {
);
console.log("redeeming NEAR on Near");
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearNEAR)
redeemMsgs = await redeemOnNear(
provider,
user2Account.accountId,
token_bridge,
transferAlgoToNearNEAR
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
let userAccount2Address = nearProviders.getTransactionLastResult(
await userAccount.functionCall({
@ -1032,7 +1078,12 @@ async function testNearSDK() {
{
console.log("redeeming P3 NEAR on Near");
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearP3)
await redeemOnNear(
provider,
user2Account.accountId,
token_bridge,
transferAlgoToNearP3
)
);
console.log(
@ -1071,9 +1122,15 @@ async function testNearSDK() {
}
console.log("YYY redeeming P3 random on Near");
console.log(
await redeemOnNear(user2Account, token_bridge, transferAlgoToNearRandoP3)
const redeemMsgs = await redeemOnNear(
provider,
user2Account.accountId,
token_bridge,
transferAlgoToNearRandoP3
);
for (const msg of redeemMsgs) {
await user2Account.functionCall(msg);
}
}
console.log(

View File

@ -22,8 +22,10 @@ import {
CHAIN_ID_NEAR,
CONTRACTS,
getEmitterAddressEth,
getEmitterAddressNear,
getSignedVAAWithRetry,
hexToUint8Array,
parseSequenceFromLogNear,
transferTokenFromNear,
uint8ArrayToHex,
} from "@certusone/wormhole-sdk";
@ -85,9 +87,7 @@ async function transferTest() {
// connect to near...
let near = await nearConnect({
headers: {},
deps: {
keyStore,
},
keyStore,
networkId: networkId as string,
nodeUrl: nearNodeUrl as string,
});
@ -120,9 +120,11 @@ async function transferTest() {
near.connection,
process.env.NEAR_ACCOUNT as string
);
const provider = userAccount.connection.provider;
let [sequence, emitterAddress] = await transferTokenFromNear(
userAccount,
const transferMsgs = await transferTokenFromNear(
provider,
userAccount.accountId,
CONTRACTS.MAINNET.near.core,
CONTRACTS.MAINNET.near.token_bridge,
NEAR_TOKEN_ADDRESS,
@ -131,12 +133,19 @@ async function transferTest() {
CHAIN_ID_ETH,
BigInt(0)
);
if (sequence === -1) {
let transferOutcome;
for (const msg of transferMsgs) {
transferOutcome = await userAccount.functionCall(msg);
}
const sequence = parseSequenceFromLogNear(transferOutcome);
if (sequence === null) {
console.log("No sequence found, check above for error");
process.exit(1);
}
const emitterAddress = getEmitterAddressNear(
CONTRACTS.MAINNET.near.token_bridge
);
console.log("emitterAddress:", emitterAddress, "sequence:", sequence);
console.log(

View File

@ -133,9 +133,7 @@ async function transferTest() {
// connect to near...
let near = await nearConnect({
headers: {},
deps: {
keyStore,
},
keyStore,
networkId: networkId as string,
nodeUrl: nearNodeUrl as string,
});
@ -292,11 +290,15 @@ async function transferTest() {
console.log("VAA received!");
console.log(uint8ArrayToHex(signedVAA));
await redeemOnNear(
userAccount,
const redeemMsgs = await redeemOnNear(
userAccount.connection.provider,
userAccount.accountId,
CONTRACTS.MAINNET.near.token_bridge,
signedVAA
);
for (const msg of redeemMsgs) {
await userAccount.functionCall(msg);
}
console.log("Redeemed!");

View File

@ -19,7 +19,7 @@
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
"near-api-js": "^0.45.1"
"near-api-js": "^1.0.0"
},
"devDependencies": {
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
@ -10097,11 +10097,11 @@
"dev": true
},
"node_modules/near-api-js": {
"version": "0.45.1",
"resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-0.45.1.tgz",
"integrity": "sha512-QyPO/vjvMFlcMO1DCpsqzmnSqPIyHsjK1Qi4B5ZR1cJCIWMkqugDF/TDf8FVQ85pmlcYeYwfiTqKanKz+3IG0A==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-1.0.0.tgz",
"integrity": "sha512-OYItaQIYlKK27FG5PrqqtkTI8Vv9TEOCi7gXePYigS4o6WofXciAXNjr4sihDJ8Vzi6s7+eEkf3zTNP3042FBw==",
"dependencies": {
"bn.js": "5.2.0",
"bn.js": "5.2.1",
"borsh": "^0.7.0",
"bs58": "^4.0.0",
"depd": "^2.0.0",
@ -10114,11 +10114,6 @@
"tweetnacl": "^1.0.1"
}
},
"node_modules/near-api-js/node_modules/bn.js": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw=="
},
"node_modules/near-api-js/node_modules/borsh": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz",
@ -21156,11 +21151,11 @@
"dev": true
},
"near-api-js": {
"version": "0.45.1",
"resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-0.45.1.tgz",
"integrity": "sha512-QyPO/vjvMFlcMO1DCpsqzmnSqPIyHsjK1Qi4B5ZR1cJCIWMkqugDF/TDf8FVQ85pmlcYeYwfiTqKanKz+3IG0A==",
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-1.0.0.tgz",
"integrity": "sha512-OYItaQIYlKK27FG5PrqqtkTI8Vv9TEOCi7gXePYigS4o6WofXciAXNjr4sihDJ8Vzi6s7+eEkf3zTNP3042FBw==",
"requires": {
"bn.js": "5.2.0",
"bn.js": "5.2.1",
"borsh": "^0.7.0",
"bs58": "^4.0.0",
"depd": "^2.0.0",
@ -21173,11 +21168,6 @@
"tweetnacl": "^1.0.1"
},
"dependencies": {
"bn.js": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz",
"integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw=="
},
"borsh": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz",

View File

@ -68,6 +68,6 @@
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
"near-api-js": "^0.45.1"
"near-api-js": "^1.0.0"
}
}

View File

@ -1,10 +1,15 @@
import { PublicKey } from "@solana/web3.js";
import { decodeAddress, getApplicationAddress } from "algosdk";
import { bech32 } from "bech32";
import { arrayify, BytesLike, Hexable, zeroPad } from "ethers/lib/utils";
import {
arrayify,
sha256,
BytesLike,
Hexable,
zeroPad,
} from "ethers/lib/utils";
import { importTokenWasm } from "../solana/wasm";
import { uint8ArrayToHex } from "../utils";
import { sha256 } from "js-sha256";
export function getEmitterAddressEth(
contractAddress: number | BytesLike | Hexable
@ -35,5 +40,5 @@ export function getEmitterAddressAlgorand(appId: bigint): string {
}
export function getEmitterAddressNear(programAddress: string): string {
return sha256.hex(programAddress);
return uint8ArrayToHex(arrayify(sha256(Buffer.from(programAddress, "utf8"))));
}

View File

@ -1,6 +1,7 @@
import { TransactionResponse } from "@solana/web3.js";
import { TxInfo } from "@terra-money/terra.js";
import { BigNumber, ContractReceipt } from "ethers";
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
import { Implementation__factory } from "../ethers-contracts";
export function parseSequenceFromLogEth(
@ -123,17 +124,20 @@ export function parseSequenceFromLogAlgorand(
return sequence;
}
export function parseSequenceFromLogNear(result: any): [number, string] {
let sequence = "";
for (const o of result.receipts_outcome) {
const NEAR_EVENT_PREFIX = "EVENT_JSON:";
export function parseSequenceFromLogNear(
outcome: FinalExecutionOutcome
): string | null {
for (const o of outcome.receipts_outcome) {
for (const l of o.outcome.logs) {
if (l.startsWith("EVENT_JSON:")) {
const body = JSON.parse(l.slice(11));
if (l.startsWith(NEAR_EVENT_PREFIX)) {
const body = JSON.parse(l.slice(NEAR_EVENT_PREFIX.length));
if (body.standard === "wormhole" && body.event === "publish") {
return [body.seq, body.emitter];
return body.seq.toString();
}
}
}
}
return [-1, ""];
return null;
}

View File

@ -49,7 +49,7 @@ export async function getIsTransferCompletedTerra(
gasPrices,
}
);
} catch (e) {
} catch (e: any) {
// redeemed if the VAA was already executed
return e.response.data.message.includes("VaaAlreadyExecuted");
}

View File

@ -83,6 +83,8 @@ export const TERRA_HOST =
isClassic: true,
};
export const NEAR_NODE_URL = ci ? "http://near:3030" : "http://localhost:3030";
describe("consts should exist", () => {
it("has Solana test token", () => {
expect.assertions(1);

View File

@ -0,0 +1,223 @@
import { beforeAll, afterAll, expect, jest, test } from "@jest/globals";
import { ethers } from "ethers";
import { parseUnits } from "ethers/lib/utils";
import {
createWrappedOnEth,
createWrappedOnNear,
getEmitterAddressEth,
getEmitterAddressNear,
getIsTransferCompletedEth,
getIsTransferCompletedNear,
hashAccount,
hexToUint8Array,
parseSequenceFromLogEth,
parseSequenceFromLogNear,
redeemOnEth,
redeemOnNear,
registerAccount,
setDefaultWasm,
tryNativeToUint8Array,
updateWrappedOnEth,
} from "../..";
import { CHAIN_ID_ETH, CHAIN_ID_NEAR, CONTRACTS } from "../../utils/consts";
import { attestFromEth, attestNearFromNear } from "../attest";
import { approveEth, transferFromEth, transferNearFromNear } from "../transfer";
import {
ETH_NODE_URL,
ETH_PRIVATE_KEY5,
NEAR_NODE_URL,
TEST_ERC20,
} from "./consts";
import { getSignedVAABySequence } from "./helpers";
import { Account, connect, KeyPair, keyStores, Near } from "near-api-js";
import {
FinalExecutionOutcome,
getTransactionLastResult,
Provider,
} from "near-api-js/lib/providers";
import { parseNearAmount } from "near-api-js/lib/utils/format";
setDefaultWasm("node");
jest.setTimeout(60000);
let near: Near;
let nearProvider: Provider;
let account: Account;
const networkId = "sandbox";
const accountId = "devnet.test.near";
const PRIVATE_KEY =
"ed25519:nCW2EsTn91b7ettRqQX6ti8ZBNwo7tbMsenBu9nmSVG9aDhNB7hgw7S9w5M9CZu1bF23FbvhKZPfDmh2Gbs45Fs";
const ethProvider = new ethers.providers.WebSocketProvider(ETH_NODE_URL);
const signer = new ethers.Wallet(ETH_PRIVATE_KEY5, ethProvider);
const ethEmitterAddress = getEmitterAddressEth(
CONTRACTS.DEVNET.ethereum.token_bridge
);
const nearEmitterAddress = getEmitterAddressNear(
CONTRACTS.DEVNET.near.token_bridge
);
const ethTransferAmount = parseUnits("1", 18);
let ethWalletAddress: string;
beforeAll(async () => {
const keyStore = new keyStores.InMemoryKeyStore();
await keyStore.setKey(networkId, accountId, KeyPair.fromString(PRIVATE_KEY));
const config = {
keyStore,
networkId,
nodeUrl: NEAR_NODE_URL,
headers: {},
};
near = await connect(config);
nearProvider = near.connection.provider;
account = await near.account(accountId);
ethWalletAddress = await signer.getAddress();
});
afterAll(async () => {
ethProvider.destroy();
});
const nearParseLogAndGetSignedVaa = async (outcome: FinalExecutionOutcome) => {
const sequence = parseSequenceFromLogNear(outcome);
if (sequence === null) {
throw new Error("sequence is null");
}
return await getSignedVAABySequence(
CHAIN_ID_NEAR,
sequence,
nearEmitterAddress
);
};
const ethParseLogAndGetSignedVaa = async (receipt: ethers.ContractReceipt) => {
const sequence = parseSequenceFromLogEth(
receipt,
CONTRACTS.DEVNET.ethereum.core
);
return await getSignedVAABySequence(
CHAIN_ID_ETH,
sequence,
ethEmitterAddress
);
};
test("Attest and transfer Near token from Near to Ethereum", async () => {
// Attest
const attestMsg = await attestNearFromNear(
account.connection.provider,
CONTRACTS.DEVNET.near.core,
CONTRACTS.DEVNET.near.token_bridge
);
const attestOutcome = await account.functionCall(attestMsg);
const attestSignedVaa = await nearParseLogAndGetSignedVaa(attestOutcome);
try {
await createWrappedOnEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
signer,
attestSignedVaa
);
} catch {
await updateWrappedOnEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
signer,
attestSignedVaa
);
}
// Transfer
const transferMsg = await transferNearFromNear(
account.connection.provider,
CONTRACTS.DEVNET.near.core,
CONTRACTS.DEVNET.near.token_bridge,
BigInt(parseNearAmount("1") || ""),
tryNativeToUint8Array(ethWalletAddress, "ethereum"),
"ethereum",
BigInt(0)
);
const transferOutcome = await account.functionCall(transferMsg);
const transferSignedVAA = await nearParseLogAndGetSignedVaa(transferOutcome);
await redeemOnEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
signer,
transferSignedVAA
);
expect(
await getIsTransferCompletedEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
ethProvider,
transferSignedVAA
)
).toBe(true);
});
test("Attest and transfer token from Ethereum to Near", async () => {
// Attest
const attestReceipt = await attestFromEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
signer,
TEST_ERC20
);
const attestSignedVaa = await ethParseLogAndGetSignedVaa(attestReceipt);
const createWrappedMsgs = await createWrappedOnNear(
nearProvider,
CONTRACTS.DEVNET.near.token_bridge,
attestSignedVaa
);
for (const msg of createWrappedMsgs) {
await account.functionCall(msg);
}
// Transfer
await approveEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
TEST_ERC20,
signer,
ethTransferAmount
);
let { isRegistered, accountHash } = await hashAccount(
nearProvider,
CONTRACTS.DEVNET.near.token_bridge,
account.accountId
);
if (!isRegistered) {
const registerAccountMsg = registerAccount(
account.accountId,
CONTRACTS.DEVNET.near.token_bridge
);
accountHash = getTransactionLastResult(
await account.functionCall(registerAccountMsg)
);
}
const transferReceipt = await transferFromEth(
CONTRACTS.DEVNET.ethereum.token_bridge,
signer,
TEST_ERC20,
ethTransferAmount,
"near",
hexToUint8Array(accountHash)
);
const transferSignedVaa = await ethParseLogAndGetSignedVaa(transferReceipt);
const redeemMsgs = await redeemOnNear(
nearProvider,
account.accountId,
CONTRACTS.DEVNET.near.token_bridge,
transferSignedVaa
);
expect(
await getIsTransferCompletedNear(
nearProvider,
CONTRACTS.DEVNET.near.token_bridge,
transferSignedVaa
)
).toBe(false);
for (const msg of redeemMsgs) {
await account.functionCall(msg);
}
expect(
await getIsTransferCompletedNear(
nearProvider,
CONTRACTS.DEVNET.near.token_bridge,
transferSignedVaa
)
).toBe(true);
});

View File

@ -11,21 +11,26 @@ import {
OnApplicationComplete,
SuggestedParams,
} from "algosdk";
import { Account as nearAccount } from "near-api-js";
const BN = require("bn.js");
import BN from "bn.js";
import { ethers, PayableOverrides } from "ethers";
import { isNativeDenom } from "..";
import { getMessageFee, optin, TransactionSignerPair } from "../algorand";
import { Bridge__factory } from "../ethers-contracts";
import { getBridgeFeeIx, ixFromRust } from "../solana";
import { importTokenWasm } from "../solana/wasm";
import { textToHexString, textToUint8Array, uint8ArrayToHex } from "../utils";
import {
callFunctionNear,
hashAccount,
textToHexString,
textToUint8Array,
uint8ArrayToHex,
} from "../utils";
import { safeBigIntToNumber } from "../utils/bigint";
import { createNonce } from "../utils/createNonce";
import { parseSequenceFromLogNear } from "../bridge/parseSequenceFromLog";
import { getIsWrappedAssetNear } from ".";
import { isNativeDenomInjective } from "../cosmwasm";
import { Provider } from "near-api-js/lib/providers";
import { FunctionCallOptions } from "near-api-js/lib/account";
export async function attestFromEth(
tokenBridgeAddress: string,
@ -240,37 +245,23 @@ export async function attestFromAlgorand(
return txs;
}
/**
* Attest an already created asset
* If you create a new asset on near and want to transfer it elsewhere,
* you create an attestation for it on near... pass that vaa to the target chain..
* submit it.. then you can transfer from near to that target chain
* @param client An Near account client
* @param coreBridge The account for the core bridge
* @param tokenBridge The account for the token bridge
* @param asset The account for the asset
* @returns [sequenceNumber, emitter]
*/
export async function attestTokenFromNear(
client: nearAccount,
provider: Provider,
coreBridge: string,
tokenBridge: string,
asset: string
): Promise<[number, string]> {
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
// Non-signing event
): Promise<FunctionCallOptions[]> {
const options: FunctionCallOptions[] = [];
const messageFee = await callFunctionNear(
provider,
coreBridge,
"message_fee"
);
if (!getIsWrappedAssetNear(tokenBridge, asset)) {
// Non-signing event that hits the RPC
let res = await client.viewFunction(tokenBridge, "hash_account", {
account: asset,
});
// if res[0] == false, the account has not been
// registered... The first user to attest a non-wormhole token
// is gonna have to pay for the space
if (!res[0]) {
// Signing event
await client.functionCall({
const { isRegistered } = await hashAccount(provider, tokenBridge, asset);
if (!isRegistered) {
// The account has not been registered. The first user to attest a non-wormhole token pays for the space
options.push({
contractId: tokenBridge,
methodName: "register_account",
args: { account: asset },
@ -279,41 +270,28 @@ export async function attestTokenFromNear(
});
}
}
// Signing event
let result = await client.functionCall({
options.push({
contractId: tokenBridge,
methodName: "attest_token",
args: { token: asset, message_fee: message_fee },
attachedDeposit: new BN("3000000000000000000000").add(new BN(message_fee)), // 0.003 NEAR
args: { token: asset, message_fee: messageFee },
attachedDeposit: new BN("3000000000000000000000").add(new BN(messageFee)), // 0.003 NEAR
gas: new BN("100000000000000"),
});
return parseSequenceFromLogNear(result);
return options;
}
/**
* Attest NEAR
* @param client An Near account client
* @param coreBridge The account for the core bridge
* @param tokenBridge The account for the token bridge
* @returns [sequenceNumber, emitter]
*/
export async function attestNearFromNear(
client: nearAccount,
provider: Provider,
coreBridge: string,
tokenBridge: string
): Promise<[number, string]> {
let message_fee =
(await client.viewFunction(coreBridge, "message_fee", {})) + 1;
let result = await client.functionCall({
): Promise<FunctionCallOptions> {
const messageFee =
(await callFunctionNear(provider, coreBridge, "message_fee")) + 1;
return {
contractId: tokenBridge,
methodName: "attest_near",
args: { message_fee: message_fee },
attachedDeposit: new BN(message_fee),
args: { message_fee: messageFee },
attachedDeposit: new BN(messageFee),
gas: new BN("100000000000000"),
});
return parseSequenceFromLogNear(result);
};
}

View File

@ -8,11 +8,10 @@ import { Bridge__factory } from "../ethers-contracts";
import { ixFromRust } from "../solana";
import { importTokenWasm } from "../solana/wasm";
import { submitVAAOnInjective } from "./redeem";
import {
Account as nearAccount,
providers as nearProviders,
} from "near-api-js";
import BN from "bn.js";
import { FunctionCallOptions } from "near-api-js/lib/account";
import { Provider } from "near-api-js/lib/providers";
import { callFunctionNear } from "../utils";
export async function createWrappedOnEth(
tokenBridgeAddress: string,
@ -80,30 +79,25 @@ export async function createWrappedOnAlgorand(
}
export async function createWrappedOnNear(
client: nearAccount,
provider: Provider,
tokenBridge: string,
attestVAA: Uint8Array
): Promise<string> {
// Could we just pass in the vaa already as hex?
let vaa = Buffer.from(attestVAA).toString("hex");
let res = await client.viewFunction(tokenBridge, "deposit_estimates", {});
let result = await client.functionCall({
contractId: tokenBridge,
methodName: "submit_vaa",
args: { vaa: vaa },
attachedDeposit: new BN(res[1]),
gas: new BN("150000000000000"),
});
result = await client.functionCall({
contractId: tokenBridge,
methodName: "submit_vaa",
args: { vaa: vaa },
attachedDeposit: new BN(res[1]),
gas: new BN("150000000000000"),
});
return nearProviders.getTransactionLastResult(result);
): Promise<FunctionCallOptions[]> {
const vaa = Buffer.from(attestVAA).toString("hex");
const res = await callFunctionNear(
provider,
tokenBridge,
"deposit_estimates"
);
const msgs = [
{
contractId: tokenBridge,
methodName: "submit_vaa",
args: { vaa },
attachedDeposit: new BN(res[1]),
gas: new BN("150000000000000"),
},
];
msgs.push({ ...msgs[0] });
return msgs;
}

View File

@ -1,7 +1,6 @@
import { Connection, PublicKey } from "@solana/web3.js";
import { LCDClient } from "@terra-money/terra.js";
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts";
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import { Algodv2 } from "algosdk";
import { ethers } from "ethers";
import { fromUint8Array } from "js-base64";
@ -13,13 +12,13 @@ import {
import { Bridge__factory } from "../ethers-contracts";
import { importTokenWasm } from "../solana/wasm";
import {
callFunctionNear,
ChainId,
ChainName,
CHAIN_ID_ALGORAND,
coalesceChainId,
} from "../utils";
import { Account as nearAccount } from "near-api-js";
const BN = require("bn.js");
import { Provider } from "near-api-js/lib/providers";
/**
* Returns a foreign asset address on Ethereum for a provided native chain and asset address, AddressZero if it does not exist
@ -165,17 +164,19 @@ export async function getForeignAssetAlgorand(
}
export async function getForeignAssetNear(
client: nearAccount,
provider: Provider,
tokenAccount: string,
chain: ChainId | ChainName,
contract: string
): Promise<string | null> {
const chainId = coalesceChainId(chain);
let ret = await client.viewFunction(tokenAccount, "get_foreign_asset", {
chain: chainId,
address: contract,
});
if (ret === "") return null;
else return ret;
const ret = await callFunctionNear(
provider,
tokenAccount,
"get_foreign_asset",
{
chain: coalesceChainId(chain),
address: contract,
}
);
return ret !== "" ? ret : null;
}

View File

@ -2,7 +2,6 @@ import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts";
import { Connection, PublicKey } from "@solana/web3.js";
import { LCDClient } from "@terra-money/terra.js";
import { Algodv2, bigIntToBytes } from "algosdk";
import { Account as nearAccount } from "near-api-js";
import axios from "axios";
import { ethers } from "ethers";
import { fromUint8Array } from "js-base64";
@ -14,10 +13,12 @@ import {
MAX_BITS,
_parseVAAAlgorand,
} from "../algorand";
import { callFunctionNear } from "../utils/near";
import { getSignedVAAHash } from "../bridge";
import { Bridge__factory } from "../ethers-contracts";
import { importCoreWasm } from "../solana/wasm";
import { safeBigIntToNumber } from "../utils/bigint";
import { Provider } from "near-api-js/lib/providers";
export async function getIsTransferCompletedEth(
tokenBridgeAddress: string,
@ -220,24 +221,15 @@ export async function getIsTransferCompletedAlgorand(
return retVal;
}
/**
* <p>Returns true if this transfer was completed on Near</p>
* @param near account
* @param tokenAccount the Token bridge account
* @param signedVAA VAA to check
* @returns true if VAA has been redeemed, false otherwise
*/
export async function getIsTransferCompletedNear(
client: nearAccount,
tokenAccount: string,
provider: Provider,
tokenBridge: string,
signedVAA: Uint8Array
): Promise<boolean> {
// Could we just pass in the vaa already as hex?
let vaa = Buffer.from(signedVAA).toString("hex");
const vaa = Buffer.from(signedVAA).toString("hex");
return (
await client.viewFunction(tokenAccount, "is_transfer_completed", {
vaa: vaa,
await callFunctionNear(provider, tokenBridge, "is_transfer_completed", {
vaa,
})
)[1];
}

View File

@ -108,12 +108,6 @@ export async function getIsWrappedAssetAlgorand(
return wormhole;
}
/**
* Returns whethor or not an asset on Near is a wormhole wrapped asset
* @param tokenBridge token bridge account
* @param asset Near asset account
* @returns true if the asset is wrapped
*/
export function getIsWrappedAssetNear(
tokenBridge: string,
asset: string

View File

@ -1,17 +1,14 @@
const sha256 = require("js-sha256");
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts";
import { Connection, PublicKey } from "@solana/web3.js";
import { LCDClient } from "@terra-money/terra.js";
import { Algodv2 } from "algosdk";
import { ethers } from "ethers";
import { arrayify, zeroPad } from "ethers/lib/utils";
import { arrayify, sha256, zeroPad } from "ethers/lib/utils";
import { decodeLocalState } from "../algorand";
import { buildTokenId, isNativeCosmWasmDenom } from "../cosmwasm/address";
import { TokenImplementation__factory } from "../ethers-contracts";
import { importTokenWasm } from "../solana/wasm";
import { buildNativeId, isNativeDenom } from "../terra";
import { buildNativeId } from "../terra";
import { canonicalAddress } from "../cosmos";
import {
ChainId,
@ -27,6 +24,7 @@ import {
hexToUint8Array,
coalesceCosmWasmChainId,
tryHexToNativeAssetString,
callFunctionNear,
} from "../utils";
import { safeBigIntToNumber } from "../utils/bigint";
import {
@ -34,7 +32,7 @@ import {
getIsWrappedAssetEth,
getIsWrappedAssetNear,
} from "./getIsWrappedAsset";
import { Account as nearAccount } from "near-api-js";
import { Provider } from "near-api-js/lib/providers";
// TODO: remove `as ChainId` and return number in next minor version as we can't ensure it will match our type definition
export interface WormholeWrappedInfo {
@ -269,26 +267,31 @@ export async function getOriginalAssetAlgorand(
}
export async function getOriginalAssetNear(
client: nearAccount,
provider: Provider,
tokenAccount: string,
assetAccount: string
): Promise<WormholeWrappedInfo> {
let retVal: WormholeWrappedInfo = {
const retVal: WormholeWrappedInfo = {
isWrapped: false,
chainId: CHAIN_ID_NEAR,
assetAddress: new Uint8Array(),
};
retVal.isWrapped = await getIsWrappedAssetNear(tokenAccount, assetAccount);
if (!retVal.isWrapped) {
retVal.assetAddress = sha256.sha256.hex(
Buffer.from(assetAccount).toString("hex")
);
retVal.assetAddress = assetAccount
? arrayify(sha256(Buffer.from(assetAccount)))
: zeroPad(arrayify("0x"), 32);
return retVal;
}
let buf = await client.viewFunction(tokenAccount, "get_original_asset", {
token: assetAccount,
});
const buf = await callFunctionNear(
provider,
tokenAccount,
"get_original_asset",
{
token: assetAccount,
}
);
retVal.chainId = buf[1];
retVal.assetAddress = hexToUint8Array(buf[0]);

View File

@ -18,11 +18,12 @@ import {
CHAIN_ID_NEAR,
CHAIN_ID_SOLANA,
ChainId,
ChainName,
MAX_VAA_DECIMALS,
WSOL_ADDRESS,
WSOL_DECIMALS,
uint8ArrayToHex,
callFunctionNear,
hashLookup,
} from "../utils";
import { getForeignAssetNear } from ".";
@ -31,10 +32,10 @@ import { _parseVAAAlgorand } from "../algorand";
import { hexToNativeString } from "../utils/array";
import { parseTransferPayload } from "../utils/parseVaa";
import { Account as nearAccount } from "near-api-js";
import BN from "bn.js";
import { providers as nearProviders } from "near-api-js";
import { MsgExecuteContract as MsgExecuteContractInjective } from "@injectivelabs/sdk-ts";
import { FunctionCallOptions } from "near-api-js/lib/account";
import { Provider } from "near-api-js/lib/providers";
export async function redeemOnEth(
tokenBridgeAddress: string,
@ -256,66 +257,63 @@ export async function redeemOnAlgorand(
);
}
/**
* This basically just submits the VAA to Near
* @param client
* @param tokenBridge Token bridge ID
* @param vaa The VAA to be redeemed
* @returns Transaction ID(s)
*/
export async function redeemOnNear(
client: nearAccount,
provider: Provider,
account: string,
tokenBridge: string,
vaa: Uint8Array
): Promise<String> {
let p = _parseVAAAlgorand(vaa);
): Promise<FunctionCallOptions[]> {
const options: FunctionCallOptions[] = [];
const p = _parseVAAAlgorand(vaa);
if (p.ToChain !== CHAIN_ID_NEAR) {
throw new Error("Not destined for NEAR");
}
let user = await client.viewFunction(tokenBridge, "hash_lookup", {
hash: uint8ArrayToHex(p.ToAddress as Uint8Array),
});
const { found, value: receiver } = await hashLookup(
provider,
tokenBridge,
uint8ArrayToHex(p.ToAddress as Uint8Array)
);
if (!user[0]) {
if (!found) {
throw new Error(
"Unregistered receiver (receiving account is not registered)"
);
}
user = user[1];
let token = await getForeignAssetNear(
client,
const token = await getForeignAssetNear(
provider,
tokenBridge,
p.FromChain as ChainId,
p.Contract as string
);
if (token === "") {
throw new Error("Unregistered token (this been attested yet?)");
}
if (
(p.Contract as string) !==
"0000000000000000000000000000000000000000000000000000000000000000"
) {
let bal = await client.viewFunction(token as string, "storage_balance_of", {
account_id: user,
});
if (token === "" || token === null) {
throw new Error("Unregistered token (has it been attested?)");
}
const bal = await callFunctionNear(
provider,
token as string,
"storage_balance_of",
{
account_id: receiver,
}
);
if (bal === null) {
console.log("Registering ", user, " for ", token);
bal = nearProviders.getTransactionLastResult(
await client.functionCall({
contractId: token as string,
methodName: "storage_deposit",
args: { account_id: user, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
})
);
options.push({
contractId: token as string,
methodName: "storage_deposit",
args: { account_id: receiver, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
});
}
if (
@ -328,30 +326,28 @@ export async function redeemOnNear(
)
) !== 0
) {
let bal = await client.viewFunction(
const bal = await callFunctionNear(
provider,
token as string,
"storage_balance_of",
{
account_id: client.accountId,
account_id: account,
}
);
if (bal === null) {
console.log("Registering ", client.accountId, " for ", token);
bal = nearProviders.getTransactionLastResult(
await client.functionCall({
contractId: token as string,
methodName: "storage_deposit",
args: { account_id: client.accountId, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
})
);
options.push({
contractId: token as string,
methodName: "storage_deposit",
args: { account_id: account, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
});
}
}
}
let result = await client.functionCall({
options.push({
contractId: tokenBridge,
methodName: "submit_vaa",
args: {
@ -361,7 +357,7 @@ export async function redeemOnNear(
gas: new BN("150000000000000"),
});
result = await client.functionCall({
options.push({
contractId: tokenBridge,
methodName: "submit_vaa",
args: {
@ -371,5 +367,5 @@ export async function redeemOnNear(
gas: new BN("150000000000000"),
});
return nearProviders.getTransactionLastResult(result);
return options;
}

View File

@ -20,7 +20,7 @@ import {
Transaction as AlgorandTransaction,
} from "algosdk";
import { ethers, Overrides, PayableOverrides } from "ethers";
import { isNativeDenom } from "..";
import { getIsWrappedAssetNear, isNativeDenom } from "..";
import {
assetOptinCheck,
getMessageFee,
@ -44,16 +44,13 @@ import {
textToUint8Array,
uint8ArrayToHex,
CHAIN_ID_SOLANA,
callFunctionNear,
} from "../utils";
import { safeBigIntToNumber } from "../utils/bigint";
import { isNativeDenomInjective } from "../cosmwasm";
import {
Account as nearAccount,
providers as nearProviders,
} from "near-api-js";
import { parseSequenceFromLogNear } from "../bridge/parseSequenceFromLog";
const BN = require("bn.js");
import { FunctionCallOptions } from "near-api-js/lib/account";
import { Provider } from "near-api-js/lib/providers";
export async function getAllowanceEth(
tokenBridgeAddress: string,
@ -725,21 +722,9 @@ export async function transferFromAlgorand(
return txs;
}
/**
* Transfers an asset from Near to a receiver on another chain
* @param client
* @param coreBridge account
* @param tokenBridge account of the token bridge
* @param assetId account
* @param qty Quantity to transfer
* @param receiver Receiving account
* @param chain Reeiving chain
* @param fee Transfer fee
* @param payload payload for payload3 transfers
* @returns [Sequence number of confirmation, emitter]
*/
export async function transferTokenFromNear(
client: nearAccount,
provider: Provider,
account: string,
coreBridge: string,
tokenBridge: string,
assetId: string,
@ -748,54 +733,70 @@ export async function transferTokenFromNear(
chain: ChainId | ChainName,
fee: bigint,
payload: string = ""
): Promise<[number, string]> {
let wormhole = assetId.endsWith("." + tokenBridge);
): Promise<FunctionCallOptions[]> {
const isWrapped = getIsWrappedAssetNear(tokenBridge, assetId);
let result;
const messageFee = await callFunctionNear(
provider,
coreBridge,
"message_fee",
{}
);
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
chain = coalesceChainId(chain);
if (wormhole) {
result = await client.functionCall({
contractId: tokenBridge,
methodName: "send_transfer_wormhole_token",
args: {
token: assetId,
amount: qty.toString(10),
receiver: uint8ArrayToHex(receiver),
chain: chain,
fee: fee.toString(10),
payload: payload,
message_fee: message_fee,
if (isWrapped) {
return [
{
contractId: tokenBridge,
methodName: "send_transfer_wormhole_token",
args: {
token: assetId,
amount: qty.toString(10),
receiver: uint8ArrayToHex(receiver),
chain,
fee: fee.toString(10),
payload: payload,
message_fee: messageFee,
},
attachedDeposit: new BN(messageFee + 1),
gas: new BN("100000000000000"),
},
attachedDeposit: new BN(message_fee + 1),
gas: new BN("100000000000000"),
});
];
} else {
let bal = await client.viewFunction(assetId, "storage_balance_of", {
account_id: tokenBridge,
});
const options: FunctionCallOptions[] = [];
const bal = await callFunctionNear(
provider,
assetId,
"storage_balance_of",
{
account_id: tokenBridge,
}
);
if (bal === null) {
// Looks like we have to stake some storage for this asset
// for the token bridge...
nearProviders.getTransactionLastResult(
await client.functionCall({
contractId: assetId,
methodName: "storage_deposit",
args: { account_id: tokenBridge, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
})
);
options.push({
contractId: assetId,
methodName: "storage_deposit",
args: { account_id: tokenBridge, registration_only: true },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
});
}
if (message_fee > 0) {
let bank = await client.viewFunction(tokenBridge, "bank_balance", {
acct: client.accountId,
});
if (messageFee > 0) {
const bank = await callFunctionNear(
provider,
tokenBridge,
"bank_balance",
{
acct: account,
}
);
if (!bank[0]) {
await client.functionCall({
options.push({
contractId: tokenBridge,
methodName: "register_bank",
args: {},
@ -804,18 +805,18 @@ export async function transferTokenFromNear(
});
}
if (bank[1] < message_fee) {
await client.functionCall({
if (bank[1] < messageFee) {
options.push({
contractId: tokenBridge,
methodName: "fill_bank",
args: {},
gas: new BN("100000000000000"),
attachedDeposit: new BN(message_fee),
attachedDeposit: new BN(messageFee),
});
}
}
result = await client.functionCall({
options.push({
contractId: assetId,
methodName: "ft_transfer_call",
args: {
@ -823,34 +824,22 @@ export async function transferTokenFromNear(
amount: qty.toString(10),
msg: JSON.stringify({
receiver: uint8ArrayToHex(receiver),
chain: chain,
chain,
fee: fee.toString(10),
payload: payload,
message_fee: message_fee,
message_fee: messageFee,
}),
},
attachedDeposit: new BN(1),
gas: new BN("100000000000000"),
});
}
return parseSequenceFromLogNear(result);
return options;
}
}
/**
* Transfers NEAR from Near to a receiver on another chain
* @param client
* @param coreBridge account
* @param tokenBridge account of the token bridge
* @param qty Quantity to transfer
* @param receiver Receiving account
* @param chain Reeiving chain
* @param fee Transfer fee
* @param payload payload for payload3 transfers
* @returns [Sequence number of confirmation, emitter]
*/
export async function transferNearFromNear(
client: nearAccount,
provider: Provider,
coreBridge: string,
tokenBridge: string,
qty: bigint,
@ -858,22 +847,24 @@ export async function transferNearFromNear(
chain: ChainId | ChainName,
fee: bigint,
payload: string = ""
): Promise<[number, string]> {
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
let result = await client.functionCall({
): Promise<FunctionCallOptions> {
const messageFee = await callFunctionNear(
provider,
coreBridge,
"message_fee",
{}
);
return {
contractId: tokenBridge,
methodName: "send_transfer_near",
args: {
receiver: uint8ArrayToHex(receiver),
chain: chain,
chain: coalesceChainId(chain),
fee: fee.toString(10),
payload: payload,
message_fee: message_fee,
message_fee: messageFee,
},
attachedDeposit: new BN(qty.toString(10)).add(new BN(message_fee)),
attachedDeposit: new BN(qty.toString(10)).add(new BN(messageFee)),
gas: new BN("100000000000000"),
});
return parseSequenceFromLogNear(result);
};
}

View File

@ -1,6 +1,7 @@
import { arrayify, zeroPad } from "@ethersproject/bytes";
import { PublicKey } from "@solana/web3.js";
import { hexValue, hexZeroPad, stripZeros } from "ethers/lib/utils";
import { hexValue, hexZeroPad, sha256, stripZeros } from "ethers/lib/utils";
import { Provider as NearProvider } from "near-api-js/lib/providers";
import {
hexToNativeAssetStringAlgorand,
nativeStringToHexAlgorand,
@ -28,6 +29,7 @@ import {
isTerraChain,
CHAIN_ID_PYTHNET,
} from "./consts";
import { hashLookup } from "./near";
/**
*
@ -96,7 +98,7 @@ export const tryUint8ArrayToNative = (
// wormhole-chain addresses are always 20 bytes.
return humanAddress("wormhole", a.slice(-20));
} else if (chainId === CHAIN_ID_NEAR) {
throw Error("uint8ArrayToNative: Near not supported yet.");
throw Error("uint8ArrayToNative: Use tryHexToNativeStringNear instead.");
} else if (chainId === CHAIN_ID_OSMOSIS) {
throw Error("uint8ArrayToNative: Osmosis not supported yet.");
} else if (chainId === CHAIN_ID_SUI) {
@ -112,6 +114,18 @@ export const tryUint8ArrayToNative = (
}
};
export const tryHexToNativeStringNear = async (
provider: NearProvider,
tokenBridge: string,
address: string
): Promise<string> => {
const { found, value } = await hashLookup(provider, tokenBridge, address);
if (!found) {
throw new Error("Address not found");
}
return value;
};
/**
*
* Convert an address in a wormhole's 32-byte hex representation into a chain's native
@ -214,9 +228,7 @@ export const tryNativeToHexString = (
} else if (chainId == CHAIN_ID_WORMHOLE_CHAIN) {
return uint8ArrayToHex(zeroPad(canonicalAddress(address), 32));
} else if (chainId === CHAIN_ID_NEAR) {
return uint8ArrayToHex(
zeroPad(new Uint8Array(Buffer.from(address, "ascii")), 32)
);
return uint8ArrayToHex(arrayify(sha256(Buffer.from(address))));
} else if (chainId === CHAIN_ID_OSMOSIS) {
throw Error("hexToNativeString: Osmosis not supported yet.");
} else if (chainId === CHAIN_ID_SUI) {

View File

@ -1,4 +1,7 @@
import nearAPI from "near-api-js";
import BN from "bn.js";
import { Provider } from "near-api-js/lib/providers";
import { CodeResult } from "near-api-js/lib/providers/provider";
export function logNearGas(result: any, comment: string) {
const { totalGasBurned, totalTokensBurned } = result.receipts_outcome.reduce(
@ -24,3 +27,69 @@ export function logNearGas(result: any, comment: string) {
totalTokensBurned
);
}
export async function hashAccount(
provider: Provider,
tokenBridge: string,
account: string
): Promise<{ isRegistered: boolean; accountHash: string }> {
// Near can have account names up to 64 bytes, but wormhole only supports 32
// As a result, we have to hash our account names with sha256
const [isRegistered, accountHash] = await callFunctionNear(
provider,
tokenBridge,
"hash_account",
{ account }
);
return {
isRegistered,
accountHash,
};
}
export async function hashLookup(
provider: Provider,
tokenBridge: string,
hash: string
): Promise<{ found: boolean; value: string }> {
const [found, value] = await callFunctionNear(
provider,
tokenBridge,
"hash_lookup",
{
hash,
}
);
return {
found,
value,
};
}
export function registerAccount(account: string, tokenBridge: string) {
return {
contractId: tokenBridge,
methodName: "register_account",
args: { account },
gas: new BN("100000000000000"),
attachedDeposit: new BN("2000000000000000000000"),
};
}
export async function callFunctionNear(
provider: Provider,
accountId: string,
methodName: string,
args?: any
) {
const response = await provider.query<CodeResult>({
request_type: "call_function",
account_id: accountId,
method_name: methodName,
args_base64: args
? Buffer.from(JSON.stringify(args)).toString("base64")
: "",
finality: "final",
});
return JSON.parse(Buffer.from(response.result).toString());
}

View File

@ -101,6 +101,7 @@ node build/main.js submit -c solana -n devnet "$REGISTER_TERRA_TOKEN_BRIDGE_VAA"
node build/main.js submit -c solana -n devnet "$REGISTER_BSC_TOKEN_BRIDGE_VAA"
node build/main.js submit -c solana -n devnet "$REGISTER_ALGO_TOKEN_BRIDGE_VAA"
node build/main.js submit -c solana -n devnet "$REGISTER_TERRA2_TOKEN_BRIDGE_VAA"
node build/main.js submit -c solana -n devnet "$REGISTER_NEAR_TOKEN_BRIDGE_VAA"
# Register the NFT Bridge Endpoint on ETH
node build/main.js submit -c solana -n devnet "$REGISTER_ETH_NFT_BRIDGE_VAA"
node build/main.js submit -c solana -n devnet "$REGISTER_TERRA_NFT_BRIDGE_VAA"

View File

@ -268,6 +268,8 @@ const contract_registrations = {
process.env.REGISTER_ALGO_TOKEN_BRIDGE_VAA,
// TERRA2
process.env.REGISTER_TERRA2_TOKEN_BRIDGE_VAA,
// NEAR
process.env.REGISTER_NEAR_TOKEN_BRIDGE_VAA,
],
"nft_bridge.wasm": [
// Solana