diff --git a/sdk/js/CHANGELOG.md b/sdk/js/CHANGELOG.md index 65d9de5e3e..686912ac6d 100644 --- a/sdk/js/CHANGELOG.md +++ b/sdk/js/CHANGELOG.md @@ -1,10 +1,9 @@ # Changelog ## 0.7.2 - ### Added -XPLA mainnet support +XPLA mainnet support and functions ## 0.7.1 diff --git a/sdk/js/package-lock.json b/sdk/js/package-lock.json index 6ede4531b2..e61f1c42ef 100644 --- a/sdk/js/package-lock.json +++ b/sdk/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "@certusone/wormhole-sdk", - "version": "0.7.1", + "version": "0.7.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@certusone/wormhole-sdk", - "version": "0.7.1", + "version": "0.7.2", "license": "Apache-2.0", "dependencies": { "@certusone/wormhole-sdk-proto-web": "^0.0.5", @@ -15,6 +15,7 @@ "@solana/spl-token": "^0.1.8", "@solana/web3.js": "^1.24.0", "@terra-money/terra.js": "^3.1.3", + "@xpla/xpla.js": "^0.2.1", "algosdk": "^1.15.0", "axios": "^0.24.0", "bech32": "^2.0.0", @@ -3057,6 +3058,78 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" }, + "node_modules/@xpla/xpla.js": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@xpla/xpla.js/-/xpla.js-0.2.1.tgz", + "integrity": "sha512-+2v9/rxnRaaZBShT232bB3sow0JMe5ghIzLUPy7XJQasf6fu3mmVgMUIl9QCrILkuTZwh3yNsFOvkZTPH28Fmw==", + "dependencies": { + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/signing-key": "^5.6.2", + "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", + "@terra-money/terra.proto": "^2.1.0", + "axios": "^0.26.1", + "bech32": "^2.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bufferutil": "^4.0.3", + "crypto-addr-codec": "^0.1.7", + "decimal.js": "^10.2.1", + "elliptic": "^6.5.4", + "ethereumjs-util": "^7.1.5", + "jscrypto": "^1.0.1", + "readable-stream": "^3.6.0", + "secp256k1": "^4.0.2", + "tmp": "^0.2.1", + "utf-8-validate": "^5.0.5", + "ws": "^7.5.8" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@xpla/xpla.js/node_modules/@terra-money/terra.proto": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz", + "integrity": "sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw==", + "dependencies": { + "@improbable-eng/grpc-web": "^0.14.1", + "google-protobuf": "^3.17.3", + "long": "^4.0.0", + "protobufjs": "~6.11.2" + } + }, + "node_modules/@xpla/xpla.js/node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xpla/xpla.js/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, + "node_modules/@xpla/xpla.js/node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/101": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/101/-/101-1.6.3.tgz", @@ -3642,6 +3715,14 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, + "node_modules/big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/bignumber.js": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", @@ -4500,6 +4581,20 @@ "node": ">= 8" } }, + "node_modules/crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "dependencies": { + "base-x": "^3.0.8", + "big-integer": "1.6.36", + "blakejs": "^1.1.0", + "bs58": "^4.0.1", + "ripemd160-min": "0.0.6", + "safe-buffer": "^5.2.0", + "sha3": "^2.1.1" + } + }, "node_modules/crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -11064,6 +11159,14 @@ "inherits": "^2.0.1" } }, + "node_modules/ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==", + "engines": { + "node": ">=8" + } + }, "node_modules/rlp": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", @@ -11269,7 +11372,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "dev": true, "dependencies": { "buffer": "6.0.3" } @@ -11278,7 +11380,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "funding": [ { "type": "github", @@ -13317,9 +13418,9 @@ } }, "node_modules/ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "engines": { "node": ">=8.3.0" }, @@ -15769,6 +15870,74 @@ } } }, + "@xpla/xpla.js": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@xpla/xpla.js/-/xpla.js-0.2.1.tgz", + "integrity": "sha512-+2v9/rxnRaaZBShT232bB3sow0JMe5ghIzLUPy7XJQasf6fu3mmVgMUIl9QCrILkuTZwh3yNsFOvkZTPH28Fmw==", + "requires": { + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/keccak256": "^5.6.1", + "@ethersproject/signing-key": "^5.6.2", + "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7", + "@terra-money/terra.proto": "^2.1.0", + "axios": "^0.26.1", + "bech32": "^2.0.0", + "bip32": "^2.0.6", + "bip39": "^3.0.3", + "bufferutil": "^4.0.3", + "crypto-addr-codec": "^0.1.7", + "decimal.js": "^10.2.1", + "elliptic": "^6.5.4", + "ethereumjs-util": "^7.1.5", + "jscrypto": "^1.0.1", + "readable-stream": "^3.6.0", + "secp256k1": "^4.0.2", + "tmp": "^0.2.1", + "utf-8-validate": "^5.0.5", + "ws": "^7.5.8" + }, + "dependencies": { + "@terra-money/terra.proto": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz", + "integrity": "sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw==", + "requires": { + "@improbable-eng/grpc-web": "^0.14.1", + "google-protobuf": "^3.17.3", + "long": "^4.0.0", + "protobufjs": "~6.11.2" + } + }, + "@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "requires": { + "@types/node": "*" + } + }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, + "ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + } + } + } + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -16216,6 +16385,11 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, + "big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" + }, "bignumber.js": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", @@ -16943,6 +17117,20 @@ "which": "^2.0.1" } }, + "crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "requires": { + "base-x": "^3.0.8", + "big-integer": "1.6.36", + "blakejs": "^1.1.0", + "bs58": "^4.0.1", + "ripemd160-min": "0.0.6", + "safe-buffer": "^5.2.0", + "sha3": "^2.1.1" + } + }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -21930,6 +22118,11 @@ "inherits": "^2.0.1" } }, + "ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==" + }, "rlp": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", @@ -22093,7 +22286,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "dev": true, "requires": { "buffer": "6.0.3" }, @@ -22102,7 +22294,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, "requires": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -23722,9 +23913,9 @@ } }, "ws": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "requires": {} }, "xhr": { diff --git a/sdk/js/package.json b/sdk/js/package.json index 4be32d232d..63557bb7dd 100644 --- a/sdk/js/package.json +++ b/sdk/js/package.json @@ -64,6 +64,7 @@ "@solana/spl-token": "^0.1.8", "@solana/web3.js": "^1.24.0", "@terra-money/terra.js": "^3.1.3", + "@xpla/xpla.js": "^0.2.1", "algosdk": "^1.15.0", "axios": "^0.24.0", "bech32": "^2.0.0", diff --git a/sdk/js/src/bridge/getEmitterAddress.ts b/sdk/js/src/bridge/getEmitterAddress.ts index 7c23374ea1..034c587f6d 100644 --- a/sdk/js/src/bridge/getEmitterAddress.ts +++ b/sdk/js/src/bridge/getEmitterAddress.ts @@ -32,6 +32,8 @@ export async function getEmitterAddressTerra(programAddress: string) { export const getEmitterAddressInjective = getEmitterAddressTerra; +export const getEmitterAddressXpla = getEmitterAddressTerra; + export function getEmitterAddressAlgorand(appId: bigint): string { const appAddr: string = getApplicationAddress(appId); const decAppAddr: Uint8Array = decodeAddress(appAddr).publicKey; diff --git a/sdk/js/src/bridge/parseSequenceFromLog.ts b/sdk/js/src/bridge/parseSequenceFromLog.ts index 4574aff995..6694dd8895 100644 --- a/sdk/js/src/bridge/parseSequenceFromLog.ts +++ b/sdk/js/src/bridge/parseSequenceFromLog.ts @@ -1,5 +1,6 @@ import { TransactionResponse } from "@solana/web3.js"; import { TxInfo } from "@terra-money/terra.js"; +import { TxInfo as XplaTxInfo } from "@xpla/xpla.js"; import { BigNumber, ContractReceipt } from "ethers"; import { FinalExecutionOutcome } from "near-api-js/lib/providers"; import { Implementation__factory } from "../ethers-contracts"; @@ -51,6 +52,23 @@ export function parseSequenceFromLogTerra(info: TxInfo): string { return sequence.toString(); } +export function parseSequenceFromLogXpla(info: XplaTxInfo): string { + // Scan for the Sequence attribute in all the outputs of the transaction. + // TODO: Make this not horrible. + let sequence = ""; + const jsonLog = JSON.parse(info.raw_log); + jsonLog.map((row: any) => { + row.events.map((event: any) => { + event.attributes.map((attribute: any) => { + if (attribute.key === "message.sequence") { + sequence = attribute.value; + } + }); + }); + }); + return sequence.toString(); +} + export function parseSequencesFromLogTerra(info: TxInfo): string[] { // Scan for the Sequence attribute in all the outputs of the transaction. // TODO: Make this not horrible. diff --git a/sdk/js/src/cosmwasm/address.ts b/sdk/js/src/cosmwasm/address.ts index d0626eb6a1..11ae101c39 100644 --- a/sdk/js/src/cosmwasm/address.ts +++ b/sdk/js/src/cosmwasm/address.ts @@ -1,3 +1,5 @@ +import { LCDClient as TerraLCDClient } from "@terra-money/terra.js"; +import { LCDClient as XplaLCDClient } from "@xpla/xpla.js"; import { keccak256 } from "ethers/lib/utils"; import { isNativeDenom } from "../terra"; import { @@ -10,8 +12,8 @@ import { isTerraChain, } from "../utils"; -export const isNativeDenomInjective = (string = "") => string === "inj"; -export const isNativeDenomXpla = (string = "") => string === "axpla"; +export const isNativeDenomInjective = (denom: string) => denom === "inj"; +export const isNativeDenomXpla = (denom: string) => denom === "axpla"; export function isNativeCosmWasmDenom( chainId: CosmWasmChainId, @@ -37,3 +39,44 @@ export function buildTokenId( keccak256(Buffer.from(address, "utf-8")).substring(4) ); } +export interface ExternalIdResponse { + token_id: { + Bank?: { denom: string }; + Contract?: { + NativeCW20?: { + contract_address: string; + }; + ForeignToken?: { + chain_id: string; + foreign_address: string; + }; + }; + }; +} + +// returns the TokenId corresponding to the ExternalTokenId +// see cosmwasm token_addresses.rs +export const queryExternalId = async ( + client: TerraLCDClient | XplaLCDClient, + tokenBridgeAddress: string, + externalTokenId: string +) => { + try { + const response = await client.wasm.contractQuery( + tokenBridgeAddress, + { + external_id: { + external_id: Buffer.from(externalTokenId, "hex").toString("base64"), + }, + } + ); + return ( + // response depends on the token type + response.token_id.Bank?.denom || + response.token_id.Contract?.NativeCW20?.contract_address || + response.token_id.Contract?.ForeignToken?.foreign_address + ); + } catch { + return null; + } +}; diff --git a/sdk/js/src/index.ts b/sdk/js/src/index.ts index 51e273d6e0..f9b6e2d4e7 100644 --- a/sdk/js/src/index.ts +++ b/sdk/js/src/index.ts @@ -6,6 +6,7 @@ export * from "./rpc"; export * from "./utils"; export * from "./bridge"; export * from "./token_bridge"; +export * from "./cosmwasm"; export * as cosmos from "./cosmos"; export * as ethers_contracts from "./ethers-contracts"; diff --git a/sdk/js/src/token_bridge/attest.ts b/sdk/js/src/token_bridge/attest.ts index 3e0524b9ce..db277f1f0a 100644 --- a/sdk/js/src/token_bridge/attest.ts +++ b/sdk/js/src/token_bridge/attest.ts @@ -28,9 +28,10 @@ import { import { safeBigIntToNumber } from "../utils/bigint"; import { createNonce } from "../utils/createNonce"; import { getIsWrappedAssetNear } from "."; -import { isNativeDenomInjective } from "../cosmwasm"; +import { isNativeDenomInjective, isNativeDenomXpla } from "../cosmwasm"; import { Provider } from "near-api-js/lib/providers"; import { FunctionCallOptions } from "near-api-js/lib/account"; +import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js"; export async function attestFromEth( tokenBridgeAddress: string, @@ -102,6 +103,29 @@ export async function attestFromInjective( }); } +export function attestFromXpla( + tokenBridgeAddress: string, + walletAddress: string, + asset: string +): XplaMsgExecuteContract { + const nonce = Math.round(Math.random() * 100000); + const isNativeAsset = isNativeDenomXpla(asset); + return new XplaMsgExecuteContract(walletAddress, tokenBridgeAddress, { + create_asset_meta: { + asset_info: isNativeAsset + ? { + native_token: { denom: asset }, + } + : { + token: { + contract_addr: asset, + }, + }, + nonce: nonce, + }, + }); +} + export async function attestFromSolana( connection: Connection, bridgeAddress: string, diff --git a/sdk/js/src/token_bridge/createWrapped.ts b/sdk/js/src/token_bridge/createWrapped.ts index c50c9666b9..5f65b8c8cd 100644 --- a/sdk/js/src/token_bridge/createWrapped.ts +++ b/sdk/js/src/token_bridge/createWrapped.ts @@ -12,6 +12,7 @@ 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"; +import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js"; export async function createWrappedOnEth( tokenBridgeAddress: string, @@ -39,6 +40,18 @@ export async function createWrappedOnTerra( export const createWrappedOnInjective = submitVAAOnInjective; +export function createWrappedOnXpla( + tokenBridgeAddress: string, + walletAddress: string, + signedVAA: Uint8Array +): XplaMsgExecuteContract { + return new XplaMsgExecuteContract(walletAddress, tokenBridgeAddress, { + submit_vaa: { + data: fromUint8Array(signedVAA), + }, + }); +} + export async function createWrappedOnSolana( connection: Connection, bridgeAddress: string, diff --git a/sdk/js/src/token_bridge/getForeignAsset.ts b/sdk/js/src/token_bridge/getForeignAsset.ts index 3c04acaae2..0f1df1f549 100644 --- a/sdk/js/src/token_bridge/getForeignAsset.ts +++ b/sdk/js/src/token_bridge/getForeignAsset.ts @@ -19,6 +19,7 @@ import { coalesceChainId, } from "../utils"; import { Provider } from "near-api-js/lib/providers"; +import { LCDClient as XplaLCDClient } from "@xpla/xpla.js"; /** * Returns a foreign asset address on Ethereum for a provided native chain and asset address, AddressZero if it does not exist @@ -105,6 +106,28 @@ export async function getForeignAssetInjective( } } +export async function getForeignAssetXpla( + tokenBridgeAddress: string, + client: XplaLCDClient, + originChain: ChainId | ChainName, + originAsset: Uint8Array +): Promise { + try { + const result: { address: string } = await client.wasm.contractQuery( + tokenBridgeAddress, + { + wrapped_registry: { + chain: coalesceChainId(originChain), + address: fromUint8Array(originAsset), + }, + } + ); + return result.address; + } catch (e) { + return null; + } +} + /** * Returns a foreign asset address on Solana for a provided native chain and asset address * @param connection diff --git a/sdk/js/src/token_bridge/getIsTransferCompleted.ts b/sdk/js/src/token_bridge/getIsTransferCompleted.ts index 1da62aa880..02d346679b 100644 --- a/sdk/js/src/token_bridge/getIsTransferCompleted.ts +++ b/sdk/js/src/token_bridge/getIsTransferCompleted.ts @@ -19,6 +19,7 @@ import { Bridge__factory } from "../ethers-contracts"; import { importCoreWasm } from "../solana/wasm"; import { safeBigIntToNumber } from "../utils/bigint"; import { Provider } from "near-api-js/lib/providers"; +import { LCDClient as XplaLCDClient } from "@xpla/xpla.js"; export async function getIsTransferCompletedEth( tokenBridgeAddress: string, @@ -126,6 +127,22 @@ export async function getIsTransferCompletedInjective( return false; } +export async function getIsTransferCompletedXpla( + tokenBridgeAddress: string, + signedVAA: Uint8Array, + client: XplaLCDClient +): Promise { + const result: { is_redeemed: boolean } = await client.wasm.contractQuery( + tokenBridgeAddress, + { + is_vaa_redeemed: { + vaa: fromUint8Array(signedVAA), + }, + } + ); + return result.is_redeemed; +} + export async function getIsTransferCompletedSolana( tokenBridgeAddress: string, signedVAA: Uint8Array, diff --git a/sdk/js/src/token_bridge/getOriginalAsset.ts b/sdk/js/src/token_bridge/getOriginalAsset.ts index 14981448a5..6e14581bd6 100644 --- a/sdk/js/src/token_bridge/getOriginalAsset.ts +++ b/sdk/js/src/token_bridge/getOriginalAsset.ts @@ -1,6 +1,6 @@ import { ChainGrpcWasmApi } from "@injectivelabs/sdk-ts"; import { Connection, PublicKey } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; +import { LCDClient as TerraLCDClient } from "@terra-money/terra.js"; import { Algodv2 } from "algosdk"; import { ethers } from "ethers"; import { arrayify, sha256, zeroPad } from "ethers/lib/utils"; @@ -33,6 +33,7 @@ import { getIsWrappedAssetNear, } from "./getIsWrappedAsset"; import { Provider } from "near-api-js/lib/providers"; +import { LCDClient as XplaLCDClient } from "@xpla/xpla.js"; // 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 { @@ -80,7 +81,7 @@ export async function getOriginalAssetEth( } export async function getOriginalAssetTerra( - client: LCDClient, + client: TerraLCDClient, wrappedAddress: string ) { return getOriginalAssetCosmWasm(client, wrappedAddress, CHAIN_ID_TERRA); @@ -140,8 +141,15 @@ export async function getOriginalAssetInjective( }; } +export async function getOriginalAssetXpla( + client: XplaLCDClient, + wrappedAddress: string +) { + return getOriginalAssetCosmWasm(client, wrappedAddress, "xpla"); +} + export async function getOriginalAssetCosmWasm( - client: LCDClient, + client: TerraLCDClient | XplaLCDClient, wrappedAddress: string, lookupChain: CosmWasmChainId | CosmWasmChainName ): Promise { diff --git a/sdk/js/src/token_bridge/redeem.ts b/sdk/js/src/token_bridge/redeem.ts index fe4b7a3a29..24d380b87b 100644 --- a/sdk/js/src/token_bridge/redeem.ts +++ b/sdk/js/src/token_bridge/redeem.ts @@ -36,6 +36,7 @@ import BN from "bn.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"; +import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js"; export async function redeemOnEth( tokenBridgeAddress: string, @@ -96,6 +97,18 @@ export async function submitVAAOnInjective( } export const redeemOnInjective = submitVAAOnInjective; +export function redeemOnXpla( + tokenBridgeAddress: string, + walletAddress: string, + signedVAA: Uint8Array +): XplaMsgExecuteContract { + return new XplaMsgExecuteContract(walletAddress, tokenBridgeAddress, { + submit_vaa: { + data: fromUint8Array(signedVAA), + }, + }); +} + export async function redeemAndUnwrapOnSolana( connection: Connection, bridgeAddress: string, diff --git a/sdk/js/src/token_bridge/transfer.ts b/sdk/js/src/token_bridge/transfer.ts index 0909bf494d..10ce082f1d 100644 --- a/sdk/js/src/token_bridge/transfer.ts +++ b/sdk/js/src/token_bridge/transfer.ts @@ -47,10 +47,11 @@ import { callFunctionNear, } from "../utils"; import { safeBigIntToNumber } from "../utils/bigint"; -import { isNativeDenomInjective } from "../cosmwasm"; +import { isNativeDenomInjective, isNativeDenomXpla } from "../cosmwasm"; const BN = require("bn.js"); import { FunctionCallOptions } from "near-api-js/lib/account"; import { Provider } from "near-api-js/lib/providers"; +import { MsgExecuteContract as XplaMsgExecuteContract } from "@xpla/xpla.js"; export async function getAllowanceEth( tokenBridgeAddress: string, @@ -330,6 +331,95 @@ export async function transferFromInjective( ]; } +export function transferFromXpla( + walletAddress: string, + tokenBridgeAddress: string, + tokenAddress: string, + amount: string, + recipientChain: ChainId | ChainName, + recipientAddress: Uint8Array, + relayerFee: string = "0", + payload: Uint8Array | null = null +): XplaMsgExecuteContract[] { + const recipientChainId = coalesceChainId(recipientChain); + const nonce = Math.round(Math.random() * 100000); + const isNativeAsset = isNativeDenomXpla(tokenAddress); + const createInitiateTransfer = (info: object) => + payload + ? { + initiate_transfer_with_payload: { + asset: { + amount, + info, + }, + recipient_chain: recipientChainId, + recipient: Buffer.from(recipientAddress).toString("base64"), + fee: relayerFee, + nonce, + payload, + }, + } + : { + initiate_transfer: { + asset: { + amount, + info, + }, + recipient_chain: recipientChainId, + recipient: Buffer.from(recipientAddress).toString("base64"), + fee: relayerFee, + nonce, + }, + }; + return isNativeAsset + ? [ + new XplaMsgExecuteContract( + walletAddress, + tokenBridgeAddress, + { + deposit_tokens: {}, + }, + { [tokenAddress]: amount } + ), + new XplaMsgExecuteContract( + walletAddress, + tokenBridgeAddress, + createInitiateTransfer({ + native_token: { + denom: tokenAddress, + }, + }), + {} + ), + ] + : [ + new XplaMsgExecuteContract( + walletAddress, + tokenAddress, + { + increase_allowance: { + spender: tokenBridgeAddress, + amount: amount, + expires: { + never: {}, + }, + }, + }, + {} + ), + new XplaMsgExecuteContract( + walletAddress, + tokenBridgeAddress, + createInitiateTransfer({ + token: { + contract_addr: tokenAddress, + }, + }), + {} + ), + ]; +} + export async function transferNativeSol( connection: Connection, bridgeAddress: string, diff --git a/sdk/js/src/token_bridge/updateWrapped.ts b/sdk/js/src/token_bridge/updateWrapped.ts index f2ff9bea34..c5c6ef9ce1 100644 --- a/sdk/js/src/token_bridge/updateWrapped.ts +++ b/sdk/js/src/token_bridge/updateWrapped.ts @@ -5,6 +5,7 @@ import { createWrappedOnTerra, createWrappedOnNear, submitVAAOnInjective, + createWrappedOnXpla, } from "."; import { Bridge__factory } from "../ethers-contracts"; @@ -24,6 +25,8 @@ export const updateWrappedOnTerra = createWrappedOnTerra; export const updateWrappedOnInjective = submitVAAOnInjective; +export const updateWrappedOnXpla = createWrappedOnXpla; + export const updateWrappedOnSolana = createWrappedOnSolana; export const updateWrappedOnAlgorand = createWrappedOnAlgorand;