Add framework for Terra contract testing (#1050)
* Add terra integration test framework * Update README * Remove comment * Update README * Add .gitignore
This commit is contained in:
parent
e1f4b8e10b
commit
6a00c3b44c
|
@ -54,6 +54,15 @@ jobs:
|
|||
node-version: '16'
|
||||
- run: cd ethereum && make test
|
||||
|
||||
terra:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16'
|
||||
- run: cd terra && make test
|
||||
|
||||
# Run linters, Go tests and other outside-of-Tilt things.
|
||||
lint-and-tests:
|
||||
# The linter is slow enough that we want to run it on the self-hosted runner
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
LocalTerra
|
||||
artifacts
|
||||
!artifacts/cw20_base.wasm
|
|
@ -53,7 +53,24 @@ deploy/nft_bridge: nft_bridge-code-id-$(NETWORK).txt
|
|||
tools/node_modules: tools/package-lock.json
|
||||
cd tools && npm ci
|
||||
|
||||
LocalTerra:
|
||||
git clone --depth 1 https://www.github.com/terra-money/LocalTerra
|
||||
|
||||
test/node_modules: test/package-lock.json
|
||||
cd test && npm ci
|
||||
|
||||
.PHONY: test
|
||||
## Run integration test
|
||||
test: artifacts test/node_modules LocalTerra
|
||||
@if pgrep terrad; then echo "Error: terrad already running. Stop it before running tests"; exit 1; fi
|
||||
cd LocalTerra && docker compose up --detach
|
||||
sleep 5
|
||||
cd test && npm run test || (cd ../LocalTerra && docker compose down && exit 1)
|
||||
cd LocalTerra && docker compose down
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(WASMS)
|
||||
rm -f artifacts/checksums.txt
|
||||
rm -rf tools/node_modules
|
||||
rm -rf test/node_modules
|
||||
|
|
|
@ -39,6 +39,20 @@ wormhole/terra $ cat artifacts/checksums.txt
|
|||
|
||||
Once you have verified the Terra contracts are deterministic with a peer, you can now move to the deploy step.
|
||||
|
||||
## Run tests
|
||||
|
||||
**Disclaimer: Currently the only test that exists is for the token bridge's transfer.**
|
||||
|
||||
You can run the integration test suite on the artifacts you built.
|
||||
|
||||
```console
|
||||
wormhole/terra $ make test
|
||||
```
|
||||
|
||||
This command deploys your artifacts and performs various interactions with your
|
||||
contracts in a LocalTerra node. Any new functionality (including expected errors)
|
||||
to the contracts should be added to this test suite.
|
||||
|
||||
## Deploy Contracts
|
||||
|
||||
Now that you have built and verified checksums, you can now deploy one or more relevant contracts to the Terra blockchain.
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Wormhole Contract Test Suite
|
||||
|
||||
## Running Local Terra Node
|
||||
|
||||
In order to run these tests, you need to have a local Terra node running. These tests are meant to be run using [LocalTerra](https://github.com/terra-money/LocalTerra). This requires [Docker Compose](https://docs.docker.com/compose/install/) to run. You can also run _terrad_ with the same set up Tilt uses (see configuration [here](../../devnet/terra-devnet.yaml)).
|
||||
|
||||
## Build
|
||||
|
||||
In the [terra root directory](../), run the following:
|
||||
```sh
|
||||
make artifacts
|
||||
```
|
||||
|
||||
## Run the Test Suite
|
||||
|
||||
The easy way would be to navigate to the [terra root directory](../), run the following:
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
|
||||
If you plan on adding new tests and plan on persisting LocalTerra, make sure dependencies are installed:
|
||||
```sh
|
||||
npm ci
|
||||
```
|
||||
|
||||
And run in this directory:
|
||||
```sh
|
||||
npm run test
|
||||
```
|
||||
|
||||
These tests are built using Jest and is meant to be structured very similarly to the [ethereum unit tests](../../ethereum), which requires running a local node via ganache before _truffle_ can run any of the testing scripts in the [test directory](../../ethereum/test).
|
||||
|
||||
**Currently the only test that exists is for the token bridge's transfer.**
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"transform": {
|
||||
"^.+\\.(t|j)sx?$": "ts-jest"
|
||||
},
|
||||
"testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
"testTimeout": 60000
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "test",
|
||||
"version": "0.1.0",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "jest --config jestconfig.json --verbose"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@cosmjs/encoding": "^0.26.2",
|
||||
"@terra-money/terra.js": "^3.0.9",
|
||||
"elliptic": "^6.5.4",
|
||||
"ts-jest": "^27.1.4",
|
||||
"web3-eth-abi": "^1.7.1",
|
||||
"web3-utils": "^1.7.1",
|
||||
"yargs": "^17.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.23",
|
||||
"prettier": "^2.6.1",
|
||||
"tslint": "^6.1.3",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,330 @@
|
|||
import { describe, expect, jest, test } from "@jest/globals";
|
||||
import { Bech32, toHex } from "@cosmjs/encoding";
|
||||
import { Int, MsgExecuteContract } from "@terra-money/terra.js";
|
||||
|
||||
import { makeProviderAndWallet, transactWithoutMemo } from "../helpers/client";
|
||||
import {
|
||||
makeGovernanceVaaPayload,
|
||||
makeTransferVaaPayload,
|
||||
signAndEncodeVaa,
|
||||
TEST_SIGNER_PKS,
|
||||
} from "../helpers/vaa";
|
||||
import { storeCode, deploy } from "../instantiate";
|
||||
|
||||
jest.setTimeout(60000);
|
||||
|
||||
const GOVERNANCE_CHAIN = 1;
|
||||
const GOVERNANCE_ADDRESS =
|
||||
"0000000000000000000000000000000000000000000000000000000000000004";
|
||||
const FOREIGN_CHAIN = 1;
|
||||
const FOREIGN_TOKEN_BRIDGE =
|
||||
"000000000000000000000000000000000000000000000000000000000000ffff";
|
||||
const GUARDIAN_SET_INDEX = 0;
|
||||
const CONSISTENCY_LEVEL = 0;
|
||||
|
||||
const WASM_WORMHOLE = "../artifacts/wormhole.wasm";
|
||||
const WASM_WRAPPED_ASSET = "../artifacts/cw20_wrapped.wasm";
|
||||
const WASM_TOKEN_BRIDGE = "../artifacts/token_bridge.wasm";
|
||||
|
||||
// global map of contract addresses for all tests
|
||||
const contracts = new Map<string, string>();
|
||||
|
||||
/*
|
||||
Mirror ethereum/test/bridge.js
|
||||
|
||||
> should be initialized with the correct signers and values
|
||||
> should register a foreign bridge implementation correctly
|
||||
> should accept a valid upgrade
|
||||
> bridged tokens should only be mint- and burn-able by owner (??)
|
||||
> should attest a token correctly
|
||||
> should correctly deploy a wrapped asset for a token attestation
|
||||
> should correctly update a wrapped asset for a token attestation
|
||||
> should deposit and log transfers correctly
|
||||
> should deposit and log fee token transfers correctly
|
||||
> should transfer out locked assets for a valid transfer vm
|
||||
> should mint bridged assets wrappers on transfer from another chain and handle fees correctly
|
||||
> should burn bridged assets wrappers on transfer to another chain
|
||||
> should handle ETH deposits correctly (uusd)
|
||||
> should handle ETH withdrawals and fees correctly (uusd)
|
||||
> should revert on transfer out of a total of > max(uint64) tokens
|
||||
|
||||
*/
|
||||
|
||||
describe("Bridge Tests", () => {
|
||||
test("Deploy Contracts", (done) => {
|
||||
(async () => {
|
||||
try {
|
||||
const [client, wallet] = await makeProviderAndWallet();
|
||||
|
||||
const governanceAddress = Buffer.from(
|
||||
GOVERNANCE_ADDRESS,
|
||||
"hex"
|
||||
).toString("base64");
|
||||
|
||||
// wormhole
|
||||
const wormhole = await deploy(client, wallet, WASM_WORMHOLE, {
|
||||
gov_chain: GOVERNANCE_CHAIN,
|
||||
gov_address: governanceAddress,
|
||||
guardian_set_expirity: 86400,
|
||||
initial_guardian_set: {
|
||||
addresses: [
|
||||
{
|
||||
bytes: Buffer.from(
|
||||
"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe",
|
||||
"hex"
|
||||
).toString("base64"),
|
||||
},
|
||||
],
|
||||
expiration_time: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// token bridge
|
||||
const wrappedAssetCodeId = await storeCode(
|
||||
client,
|
||||
wallet,
|
||||
WASM_WRAPPED_ASSET
|
||||
);
|
||||
const tokenBridge = await deploy(client, wallet, WASM_TOKEN_BRIDGE, {
|
||||
gov_chain: GOVERNANCE_CHAIN,
|
||||
gov_address: governanceAddress,
|
||||
wormhole_contract: wormhole,
|
||||
wrapped_asset_code_id: wrappedAssetCodeId,
|
||||
});
|
||||
|
||||
contracts.set("wormhole", wormhole);
|
||||
contracts.set("tokenBridge", tokenBridge);
|
||||
done();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
done("Failed to Deploy Contracts");
|
||||
}
|
||||
})();
|
||||
});
|
||||
test("Register a Foreign Bridge Implementation", (done) => {
|
||||
(async () => {
|
||||
try {
|
||||
const [client, wallet] = await makeProviderAndWallet();
|
||||
|
||||
const vaaPayload = makeGovernanceVaaPayload(
|
||||
GOVERNANCE_CHAIN,
|
||||
FOREIGN_CHAIN,
|
||||
FOREIGN_TOKEN_BRIDGE
|
||||
);
|
||||
console.info("vaaPayload", vaaPayload);
|
||||
|
||||
const timestamp = 1;
|
||||
const nonce = 1;
|
||||
const sequence = 0;
|
||||
|
||||
const signedVaa = signAndEncodeVaa(
|
||||
timestamp,
|
||||
nonce,
|
||||
GOVERNANCE_CHAIN,
|
||||
GOVERNANCE_ADDRESS,
|
||||
sequence,
|
||||
vaaPayload,
|
||||
TEST_SIGNER_PKS,
|
||||
GUARDIAN_SET_INDEX,
|
||||
CONSISTENCY_LEVEL
|
||||
);
|
||||
console.info("signedVaa", signedVaa);
|
||||
|
||||
const tokenBridge = contracts.get("tokenBridge")!;
|
||||
const submitVaa = new MsgExecuteContract(
|
||||
wallet.key.accAddress,
|
||||
tokenBridge,
|
||||
{
|
||||
submit_vaa: {
|
||||
data: Buffer.from(signedVaa, "hex").toString("base64"),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const receipt = await transactWithoutMemo(client, wallet, [submitVaa]);
|
||||
console.info("receipt", receipt.txhash);
|
||||
done();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
done("Failed to Register a Foreign Bridge Implementation");
|
||||
}
|
||||
})();
|
||||
});
|
||||
test("Initiate Transfer (native denom)", (done) => {
|
||||
(async () => {
|
||||
try {
|
||||
const [client, wallet] = await makeProviderAndWallet();
|
||||
|
||||
const tokenBridge = contracts.get("tokenBridge")!;
|
||||
|
||||
// transfer uusd
|
||||
const denom = "uusd";
|
||||
const recipientAddress =
|
||||
"0000000000000000000000004206942069420694206942069420694206942069";
|
||||
const amount = "100000000"; // one benjamin
|
||||
const relayerFee = "1000000"; // one dolla
|
||||
|
||||
const walletAddress = wallet.key.accAddress;
|
||||
|
||||
// need to deposit before initiating transfer
|
||||
const deposit = new MsgExecuteContract(
|
||||
wallet.key.accAddress,
|
||||
tokenBridge,
|
||||
{
|
||||
deposit_tokens: {},
|
||||
},
|
||||
{ [denom]: amount }
|
||||
);
|
||||
|
||||
const initiateTransfer = new MsgExecuteContract(
|
||||
walletAddress,
|
||||
tokenBridge as string,
|
||||
{
|
||||
initiate_transfer: {
|
||||
asset: {
|
||||
amount,
|
||||
info: {
|
||||
native_token: {
|
||||
denom,
|
||||
},
|
||||
},
|
||||
},
|
||||
recipient_chain: 2,
|
||||
recipient: Buffer.from(recipientAddress, "hex").toString(
|
||||
"base64"
|
||||
),
|
||||
fee: relayerFee,
|
||||
nonce: 69,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// check balances
|
||||
let balanceBefore = new Int(0);
|
||||
{
|
||||
const [balance] = await client.bank.balance(tokenBridge);
|
||||
const coin = balance.get(denom);
|
||||
if (coin !== undefined) {
|
||||
balanceBefore = new Int(coin.amount);
|
||||
}
|
||||
}
|
||||
|
||||
// execute outbound transfer
|
||||
const receipt = await transactWithoutMemo(client, wallet, [
|
||||
deposit,
|
||||
initiateTransfer,
|
||||
]);
|
||||
console.info("receipt", receipt.txhash);
|
||||
|
||||
let balanceAfter: Int;
|
||||
{
|
||||
const [balance] = await client.bank.balance(tokenBridge);
|
||||
const coin = balance.get(denom);
|
||||
expect(!coin).toBeFalsy();
|
||||
|
||||
balanceAfter = new Int(coin!.amount);
|
||||
}
|
||||
expect(
|
||||
balanceBefore.add(new Int(amount)).eq(balanceAfter)
|
||||
).toBeTruthy();
|
||||
|
||||
done();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
done("Failed to Initiate Transfer (native denom)");
|
||||
}
|
||||
})();
|
||||
});
|
||||
test("Complete Transfer (native denom)", (done) => {
|
||||
(async () => {
|
||||
try {
|
||||
const [client, wallet] = await makeProviderAndWallet();
|
||||
|
||||
const tokenBridge = contracts.get("tokenBridge")!;
|
||||
|
||||
const denom = "uusd";
|
||||
const amount = "100000000"; // one benjamin
|
||||
const relayerFee = "1000000"; // one dolla
|
||||
|
||||
const walletAddress = wallet.key.accAddress;
|
||||
const recipient = "terra17lmam6zguazs5q5u6z5mmx76uj63gldnse2pdp";
|
||||
|
||||
// check balances
|
||||
let balanceBefore = new Int(0);
|
||||
{
|
||||
const [balance] = await client.bank.balance(recipient);
|
||||
const coin = balance.get(denom);
|
||||
if (coin !== undefined) {
|
||||
balanceBefore = new Int(coin.amount);
|
||||
}
|
||||
}
|
||||
|
||||
const encodedTo = nativeToHex(recipient);
|
||||
console.log("encodedTo", encodedTo);
|
||||
const ustAddress =
|
||||
"0100000000000000000000000000000000000000000000000000000075757364";
|
||||
|
||||
const vaaPayload = makeTransferVaaPayload(
|
||||
1,
|
||||
amount,
|
||||
ustAddress,
|
||||
encodedTo,
|
||||
3,
|
||||
relayerFee,
|
||||
undefined
|
||||
);
|
||||
console.info("vaaPayload", vaaPayload);
|
||||
|
||||
const timestamp = 0;
|
||||
const nonce = 0;
|
||||
const sequence = 0;
|
||||
|
||||
const signedVaa = signAndEncodeVaa(
|
||||
timestamp,
|
||||
nonce,
|
||||
FOREIGN_CHAIN,
|
||||
FOREIGN_TOKEN_BRIDGE,
|
||||
sequence,
|
||||
vaaPayload,
|
||||
TEST_SIGNER_PKS,
|
||||
GUARDIAN_SET_INDEX,
|
||||
CONSISTENCY_LEVEL
|
||||
);
|
||||
console.info("signedVaa", signedVaa);
|
||||
|
||||
const submitVaa = new MsgExecuteContract(walletAddress, tokenBridge, {
|
||||
submit_vaa: {
|
||||
data: Buffer.from(signedVaa, "hex").toString("base64"),
|
||||
},
|
||||
});
|
||||
|
||||
// execute inbound transfer with signed vaa
|
||||
const receipt = await transactWithoutMemo(client, wallet, [submitVaa]);
|
||||
console.info("receipt", receipt.txhash);
|
||||
|
||||
let balanceAfter: Int;
|
||||
{
|
||||
const [balance] = await client.bank.balance(recipient);
|
||||
const coin = balance.get(denom);
|
||||
expect(!coin).toBeFalsy();
|
||||
|
||||
balanceAfter = new Int(coin!.amount);
|
||||
}
|
||||
const expectedAmount = (new Int(amount)).sub(relayerFee);
|
||||
expect(
|
||||
//balanceBefore.add(new Int(expectedAmount)).eq(balanceAfter)
|
||||
balanceBefore.add(expectedAmount).eq(balanceAfter)
|
||||
).toBeTruthy();
|
||||
|
||||
done();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
done("Failed to Complete Transfer (native denom)");
|
||||
}
|
||||
})();
|
||||
});
|
||||
});
|
||||
|
||||
function nativeToHex(address: string) {
|
||||
return toHex(Bech32.decode(address).data).padStart(64, "0");
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import {
|
||||
BlockTxBroadcastResult,
|
||||
LCDClient,
|
||||
MnemonicKey,
|
||||
Msg,
|
||||
Wallet,
|
||||
} from "@terra-money/terra.js";
|
||||
|
||||
export async function makeProviderAndWallet(): Promise<[LCDClient, Wallet]> {
|
||||
// provider
|
||||
const client = new LCDClient({
|
||||
URL: "http://localhost:1317",
|
||||
chainID: "localterra",
|
||||
});
|
||||
|
||||
// wallet
|
||||
const mnemonic =
|
||||
"notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius";
|
||||
|
||||
const wallet = client.wallet(
|
||||
new MnemonicKey({
|
||||
mnemonic,
|
||||
})
|
||||
);
|
||||
await wallet.sequence();
|
||||
|
||||
return [client, wallet];
|
||||
}
|
||||
|
||||
export async function transact(
|
||||
client: LCDClient,
|
||||
wallet: Wallet,
|
||||
msgs: Msg[],
|
||||
memo: string
|
||||
): Promise<BlockTxBroadcastResult> {
|
||||
const tx = await wallet.createAndSignTx({
|
||||
msgs: msgs,
|
||||
memo: memo,
|
||||
});
|
||||
|
||||
return client.tx.broadcast(tx);
|
||||
}
|
||||
|
||||
export async function transactWithoutMemo(
|
||||
client: LCDClient,
|
||||
wallet: Wallet,
|
||||
msgs: Msg[]
|
||||
): Promise<BlockTxBroadcastResult> {
|
||||
return transact(client, wallet, msgs, "");
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
import { soliditySha3 } from "web3-utils";
|
||||
|
||||
const abi = require("web3-eth-abi");
|
||||
const elliptic = require("elliptic");
|
||||
|
||||
export const TEST_SIGNER_PKS = [
|
||||
"cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0",
|
||||
];
|
||||
|
||||
export function makeGovernanceVaaPayload(
|
||||
governanceChain: number,
|
||||
foreignChain: number,
|
||||
foreignTokenBridge: string
|
||||
) {
|
||||
return (
|
||||
"000000000000000000000000000000000000000000546f6b656e427269646765" +
|
||||
abi.encodeParameter("uint16", governanceChain).substring(2 + 62) +
|
||||
"0000" +
|
||||
abi.encodeParameter("uint16", foreignChain).substring(2 + (64 - 4)) +
|
||||
foreignTokenBridge
|
||||
);
|
||||
}
|
||||
|
||||
export function makeTransferVaaPayload(
|
||||
payloadType: number,
|
||||
amount: string,
|
||||
hexlifiedTokenAddress: string,
|
||||
encodedTo: string,
|
||||
toChain: number,
|
||||
relayerFee: string,
|
||||
additionalPayload: string | undefined
|
||||
): string {
|
||||
const data =
|
||||
abi.encodeParameter("uint8", payloadType).substring(2 + 62) +
|
||||
// amount
|
||||
abi.encodeParameter("uint256", amount).substring(2) +
|
||||
// tokenaddress
|
||||
hexlifiedTokenAddress +
|
||||
// tokenchain
|
||||
"0003" + // we only care about terra-specific tokens for these tests
|
||||
// receiver
|
||||
encodedTo +
|
||||
// receiving chain
|
||||
abi.encodeParameter("uint16", toChain).substring(2 + (64 - 4)) +
|
||||
// fee
|
||||
abi.encodeParameter("uint256", relayerFee).substring(2);
|
||||
|
||||
// additional payload
|
||||
if (additionalPayload === undefined) {
|
||||
additionalPayload = "";
|
||||
}
|
||||
|
||||
return data + Buffer.from(additionalPayload, "utf8").toString("hex");
|
||||
}
|
||||
|
||||
export function signAndEncodeVaa(
|
||||
timestamp: number,
|
||||
nonce: number,
|
||||
emitterChainId: number,
|
||||
emitterAddress: string,
|
||||
sequence: number,
|
||||
data: string,
|
||||
signers: string[],
|
||||
guardianSetIndex: number,
|
||||
consistencyLevel: number
|
||||
): string {
|
||||
const body: string[] = [
|
||||
abi.encodeParameter("uint32", timestamp).substring(2 + (64 - 8)),
|
||||
abi.encodeParameter("uint32", nonce).substring(2 + (64 - 8)),
|
||||
abi.encodeParameter("uint16", emitterChainId).substring(2 + (64 - 4)),
|
||||
emitterAddress,
|
||||
abi.encodeParameter("uint64", sequence).substring(2 + (64 - 16)),
|
||||
abi.encodeParameter("uint8", consistencyLevel).substring(2 + (64 - 2)),
|
||||
data,
|
||||
];
|
||||
|
||||
const hash = soliditySha3(soliditySha3("0x" + body.join(""))!)!;
|
||||
|
||||
let signatures = "";
|
||||
|
||||
for (const i in signers) {
|
||||
const ec = new elliptic.ec("secp256k1");
|
||||
const key = ec.keyFromPrivate(signers[i]);
|
||||
const signature = key.sign(hash.substring(2), { canonical: true });
|
||||
|
||||
const packSig = [
|
||||
abi.encodeParameter("uint8", i).substring(2 + (64 - 2)),
|
||||
zeroPadBytes(signature.r.toString(16), 32),
|
||||
zeroPadBytes(signature.s.toString(16), 32),
|
||||
abi
|
||||
.encodeParameter("uint8", signature.recoveryParam)
|
||||
.substr(2 + (64 - 2)),
|
||||
];
|
||||
|
||||
signatures += packSig.join("");
|
||||
}
|
||||
|
||||
const vm = [
|
||||
abi.encodeParameter("uint8", 1).substring(2 + (64 - 2)),
|
||||
abi.encodeParameter("uint32", guardianSetIndex).substring(2 + (64 - 8)),
|
||||
abi.encodeParameter("uint8", signers.length).substring(2 + (64 - 2)),
|
||||
|
||||
signatures,
|
||||
body.join(""),
|
||||
].join("");
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
function zeroPadBytes(value: string, length: number) {
|
||||
while (value.length < 2 * length) {
|
||||
value = "0" + value;
|
||||
}
|
||||
return value;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import {
|
||||
LCDClient,
|
||||
MsgInstantiateContract,
|
||||
MsgStoreCode,
|
||||
Wallet,
|
||||
} from "@terra-money/terra.js";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
import { transactWithoutMemo } from "./helpers/client";
|
||||
|
||||
export async function storeCode(
|
||||
terra: LCDClient,
|
||||
wallet: Wallet,
|
||||
wasm: string
|
||||
): Promise<number> {
|
||||
const contract_bytes = readFileSync(wasm);
|
||||
const store_code = new MsgStoreCode(
|
||||
wallet.key.accAddress,
|
||||
contract_bytes.toString("base64")
|
||||
);
|
||||
const receipt = await transactWithoutMemo(terra, wallet, [store_code]);
|
||||
|
||||
// @ts-ignore
|
||||
const ci = /"code_id","value":"([^"]+)/gm.exec(receipt.raw_log)[1];
|
||||
return parseInt(ci);
|
||||
}
|
||||
|
||||
export async function deploy(
|
||||
terra: LCDClient,
|
||||
wallet: Wallet,
|
||||
wasm: string,
|
||||
instantiateMsg: Object
|
||||
): Promise<string> {
|
||||
const codeId = await storeCode(terra, wallet, wasm);
|
||||
|
||||
const msgs = [
|
||||
new MsgInstantiateContract(
|
||||
wallet.key.accAddress,
|
||||
wallet.key.accAddress,
|
||||
codeId,
|
||||
instantiateMsg
|
||||
),
|
||||
];
|
||||
const receipt = await transactWithoutMemo(terra, wallet, msgs);
|
||||
|
||||
// @ts-ignore
|
||||
return /"contract_address","value":"([^"]+)/gm.exec(receipt.raw_log)[1];
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue