Skip to content

Commit

Permalink
Test native transferRemote with signer
Browse files Browse the repository at this point in the history
  • Loading branch information
yorhodes committed Oct 27, 2023
1 parent 5cf0898 commit f7c4954
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 18 deletions.
1 change: 1 addition & 0 deletions typescript/sdk/src/providers/MultiProtocolProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const PROTOCOL_DEFAULT_PROVIDER_TYPE: Partial<
> = {
[ProtocolType.Ethereum]: ProviderType.EthersV5,
[ProtocolType.Sealevel]: ProviderType.SolanaWeb3,
[ProtocolType.Cosmos]: ProviderType.CosmJsWasm,
};

export interface MultiProtocolProviderOptions {
Expand Down
114 changes: 114 additions & 0 deletions typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate';
import { Secp256k1, keccak256 } from '@cosmjs/crypto';
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
import { GasPrice, SigningStargateClient } from '@cosmjs/stargate';
import { Tendermint37Client } from '@cosmjs/tendermint-rpc';

import { ProtocolType } from '@hyperlane-xyz/utils';

import {
ChainMetadata,
ChainMetadataSchema,
} from '../../metadata/chainMetadataTypes';
import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider';

import { CwHypNativeTokenAdapter } from './CosmWasmTokenAdapter';

const router =
'dual1nzkcccxw00u9egqfuuq2ue23hjj6kxmfvmc5y0r7wchk5e6nypns6768kk';

const dualitydevnet: ChainMetadata = {
name: 'dualitydevnet',
chainId: 'duality-devnet',
domainId: 33333,
protocol: ProtocolType.Cosmos,
bech32Prefix: 'dual',
slip44: 118, // what is this
rpcUrls: [
{
http: 'http://54.149.31.83:26657',
},
],
isTestnet: true,
};

const ibcDenom =
'ibc/B5CB286F69D48B2C4F6F8D8CF59011C40590DCF8A91617A5FBA9FF0A7B21307F';

const signer = '<PRIVATE_KEY>';

export async function getSigningClient() {
const wallet = await DirectSecp256k1Wallet.fromKey(
Buffer.from(signer, 'hex'),
dualitydevnet.bech32Prefix!,
);

const [account] = await wallet.getAccounts();

const clientBase = await Tendermint37Client.connect(
dualitydevnet.rpcUrls[0].http,
);

const gasPrice = GasPrice.fromString('0.025token');

const wasm = await SigningCosmWasmClient.createWithSigner(
clientBase,
wallet,
{
gasPrice,
},
);
const stargate = await SigningStargateClient.createWithSigner(
clientBase,
wallet,
{
gasPrice,
},
);

const pubkey = Secp256k1.uncompressPubkey(account.pubkey);
const ethaddr = keccak256(pubkey.slice(1)).slice(-20);

return {
wasm,
stargate,
signer: account.address,
signer_addr: Buffer.from(ethaddr).toString('hex'),
signer_pubkey: Buffer.from(account.pubkey).toString('hex'),
};
}

async function main() {
const parsed = ChainMetadataSchema.parse(dualitydevnet);
console.log({ parsed });
const multiProtocolProvider = new MultiProtocolProvider({
dualitydevnet,
});

const adapter = new CwHypNativeTokenAdapter(
dualitydevnet.name,
multiProtocolProvider,
{ router },
ibcDenom,
);
const owner = await adapter.owner();
const routers = await adapter.getAllRouters();
const domains = await adapter.getDomains();
const balance = await adapter.getBalance(owner);

console.log({ owner, routers, domains, balance });

const msg = await adapter.populateTransferRemoteTx({
destination: domains[0],
recipient: '0xE000fA4E466831dB288290Dd97e66560fb3d7d28',
weiAmountOrId: 10,
txValue: '2500000',
});

const client = await getSigningClient();

const tx = await client.wasm.executeMultiple(client.signer, [msg], 'auto');
console.log({ tx });
}

main().catch(console.error);
92 changes: 74 additions & 18 deletions typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate';
import { Coin } from '@cosmjs/stargate';

import { Address, Domain } from '@hyperlane-xyz/utils';
import {
Address,
Domain,
addressToBytes32,
strip0x,
} from '@hyperlane-xyz/utils';

import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp';
import {
Expand All @@ -16,6 +21,8 @@ import {
OwnerResponse,
RouteResponseForHexBinary,
RoutesResponseForHexBinary,
TokenType,
TokenTypeResponse,
ExecuteMsg as WarpCw20Execute,
QueryMsg as WarpCw20Query,
} from '../../cw-types/WarpCw20.types';
Expand Down Expand Up @@ -89,7 +96,6 @@ export class CwTokenAdapter
public readonly chainName: string,
public readonly multiProvider: MultiProtocolProvider,
public readonly addresses: { token: Address },
public readonly ibcDenom: string,
) {
super(chainName, multiProvider, addresses);
}
Expand Down Expand Up @@ -160,6 +166,7 @@ export class CwTokenAdapter
}

type TokenRouterResponse =
| TokenTypeResponse
| InterchainSecurityModuleResponse
| DomainsResponse
| OwnerResponse
Expand All @@ -174,9 +181,9 @@ export class CwHypTokenAdapter
public readonly chainName: ChainName,
public readonly multiProvider: MultiProtocolProvider<any>,
public readonly addresses: { token: Address; router: Address },
public readonly ibcDenom: string,
public readonly gasDenom = 'token',
) {
super(chainName, multiProvider, addresses, ibcDenom);
super(chainName, multiProvider, addresses);
}

async queryRouter<R extends TokenRouterResponse>(
Expand All @@ -198,6 +205,15 @@ export class CwHypTokenAdapter
};
}

async tokenType(): Promise<TokenType> {
const resp = await this.queryRouter<TokenTypeResponse>({
token_default: {
token_type: {},
},
});
return resp.type;
}

async interchainSecurityModule(): Promise<Address> {
throw new Error('Router does not support ISM config yet.');
}
Expand Down Expand Up @@ -259,22 +275,23 @@ export class CwHypTokenAdapter
weiAmountOrId,
txValue,
}: TransferRemoteParams): ExecuteInstruction {
if (!txValue) {
throw new Error('txValue is required for native tokens');
}
return this.prepareRouter(
{
transfer_remote: {
dest_domain: destination,
recipient,
recipient: strip0x(addressToBytes32(recipient)),
amount: weiAmountOrId.toString(),
},
},
txValue
? [
{
amount: txValue.toString(),
denom: this.ibcDenom,
},
]
: [],
[
{
amount: txValue.toString(),
denom: this.gasDenom,
},
],
);
}
}
Expand All @@ -289,14 +306,14 @@ export class CwHypNativeTokenAdapter
public readonly chainName: ChainName,
public readonly multiProvider: MultiProtocolProvider<any>,
public readonly addresses: { router: Address },
public readonly ibcDenom: string,
public readonly gasDenom: string,
) {
super(chainName, multiProvider, ibcDenom);
super(chainName, multiProvider, gasDenom);
this.cw20adapter = new CwHypTokenAdapter(
chainName,
multiProvider,
{ token: '', router: addresses.router },
ibcDenom,
gasDenom,
);
}

Expand Down Expand Up @@ -324,7 +341,46 @@ export class CwHypNativeTokenAdapter
return this.cw20adapter.quoteGasPayment(destination);
}

populateTransferRemoteTx(params: TransferRemoteParams): ExecuteInstruction {
return this.cw20adapter.populateTransferRemoteTx(params);
async denom(): Promise<string> {
const tokenType = await this.cw20adapter.tokenType();
if ('native' in tokenType) {
if ('fungible' in tokenType.native) {
return tokenType.native.fungible.denom;
}
}

throw new Error(`Token type not supported: ${tokenType}`);
}

async populateTransferRemoteTx({
destination,
recipient,
weiAmountOrId,
txValue,
}: TransferRemoteParams): Promise<ExecuteInstruction> {
if (!txValue) {
throw new Error('txValue is required for native tokens');
}

const collateralDenom = await this.denom();
return this.cw20adapter.prepareRouter(
{
transfer_remote: {
dest_domain: destination,
recipient: strip0x(addressToBytes32(recipient)),
amount: weiAmountOrId.toString(),
},
},
[
{
amount: weiAmountOrId.toString(),
denom: collateralDenom,
},
{
amount: txValue.toString(),
denom: this.gasDenom,
},
],
);
}
}

0 comments on commit f7c4954

Please sign in to comment.