Skip to content

Commit

Permalink
[WIP] add remote client
Browse files Browse the repository at this point in the history
  • Loading branch information
OscBacon committed Sep 21, 2024
1 parent 792f120 commit 1ebb4d8
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 111 deletions.
2 changes: 2 additions & 0 deletions packages/hyperlane-contracts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from "./InterchainAccountRouter.js";
export * from "./accounts/interchainAccount.js";
export * from "./actions/ica/sendTransaction.js";
export * from "./actions/ica/writeContract.js";
115 changes: 63 additions & 52 deletions packages/react-wallet-v2/src/lib/EIP155ViemLib.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { createWalletClient, custom, Account, Address, WalletClient, http, createPublicClient, zeroAddress } from 'viem'
import {
createWalletClient,
custom,
Account,
Address,
WalletClient,
Transport,
http,
createPublicClient,
zeroAddress,
Chain
} from 'viem'
import { english, generateMnemonic } from 'viem/accounts'
import { mnemonicToAccount } from 'viem/accounts'
import { providers, Wallet } from 'ethers'
import { getLocalInterchainAccount, InterchainAccount } from "@owlprotocol/hyperlane-contracts"
import {
getLocalInterchainAccount,
InterchainAccount,
sendTransaction,
writeContract
} from '@owlprotocol/hyperlane-contracts'
import { EIP155Wallet } from './EIP155Lib'
import { TransactionRequest, TransactionResponse } from '@ethersproject/providers'
import { bscTestnet, scrollSepolia } from 'viem/chains'
Expand All @@ -11,29 +27,28 @@ interface IInitArgs {
mnemonicArg?: string
}

const origin = bscTestnet;
const originChainName = "bsctestnet";
const origin = bscTestnet
const originChainName = 'bsctestnet'

const remote = scrollSepolia;
const remoteChainName = "scrollsepolia";
const remote = scrollSepolia
const remoteChainName = 'scrollsepolia'

const originAddresses = {
mailbox: '0xF9F6F5646F478d5ab4e20B0F910C92F1CCC9Cc6D',
interchainAccountIsm: '0xa9D8Ec959F34272B1a56D09AF00eeee58970d3AE',
interchainAccountRouter: '0x6d2B3e304E58c2a19f1492E7cf15CaF63Ce6e0d2'
} as const
} as const

const remoteAddresses = {
const remoteAddresses = {
mailbox: '0x3C5154a193D6e2955650f9305c8d80c18C814A68',
interchainAccountIsm: '0xE023239c8dfc172FF008D8087E7442d3eBEd9350',
interchainAccountRouter: '0xe17c37212d785760E8331D4A4395B17b34Ba8cDF'
} as const

} as const

interface InterchainAddresses {
mailbox: Address;
interchainAccountIsm: Address;
interchainAccountRouter: Address;
mailbox: Address
interchainAccountIsm: Address
interchainAccountRouter: Address
}

/**
Expand All @@ -42,17 +57,17 @@ interface InterchainAddresses {
export default class EIP155ViemLib implements EIP155Wallet {
mnemonic: string
account: Account
originWalletClient: WalletClient<Transport, Chain, Account>;
originWalletClient: WalletClient<Transport, Chain, Account>

constructor(mnemonic: string) {
this.mnemonic = mnemonic;
this.account = mnemonicToAccount(mnemonic);
this.mnemonic = mnemonic
this.account = mnemonicToAccount(mnemonic)

this.originWalletClient = createWalletClient({
chain: origin,
transport: http(),
account: this.account,
});
account: this.account
})
}

static init({ mnemonicArg }: IInitArgs) {
Expand All @@ -65,89 +80,85 @@ export default class EIP155ViemLib implements EIP155Wallet {
}

getPrivateKey(): string {
throw new Error("Unsupported getPrivateKey")
throw new Error('Unsupported getPrivateKey')
}

getAddress(): string {
return this.account.address;
async getAddress(): Promise<string> {
// return this.account.address
return Promise.resolve(zeroAddress)
}

signMessage(message: string): Promise<string> {
throw new Error("Unsupported signMessage()")
throw new Error('Unsupported signMessage()')
}

_signTypedData(domain: any, types: any, data: any, _primaryType?: string): Promise<string> {
throw new Error("Unsupported _signTypedData()")
throw new Error('Unsupported _signTypedData()')
}

connect(provider: providers.JsonRpcProvider): Wallet {
const request = function request({ method, params }: { method: string, params?: any[] }) {
const request = function request({ method, params }: { method: string; params?: any[] }) {
return provider.send(method, params ?? [])
}
const transport = custom({ request })

const walletClient = createWalletClient({
account: this.account,
transport
})

const remoteClient = createPublicClient({
transport
})

const originWalletClient = this.originWalletClient;
const originWalletClient = this.originWalletClient

const mockWallet = {
async sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {

const icaAddress = await getLocalInterchainAccount({
const icaAddress = await getLocalInterchainAccount({
publicClient: remoteClient as any,
origin: origin.id,
router: remoteAddresses.interchainAccountRouter,
owner: originWalletClient.account.address,
ism: remoteAddresses.interchainAccountIsm,
});
ism: remoteAddresses.interchainAccountIsm
})

const ica: InterchainAccount = {
address: icaAddress,
router: remoteAddresses.interchainAccountRouter,
ism: zeroAddress,
originClient: originWalletClient,
originRouter: originAddresses.interchainAccountRouter,
} as any;
originRouter: originAddresses.interchainAccountRouter
} as any

//the remote wallet client
const remoteWalletClient = createWalletClient({
chain: remote,
transport: http(),
account: ica as Account,
}).extend((client) => {
transport,
account: ica as Account
}).extend(client => {
return {
sendTransaction: (params) => sendTransaction(client, params),
writeContract: (params) => writeContract(client, params),
};
}) as unknown as WalletClient<Transport, Chain, Account>;



const hash = await walletClient.sendTransaction({
// @ts-expect-error
sendTransaction: params => sendTransaction(client, params),
// @ts-expect-error
writeContract: params => writeContract(client, params)
}
}) as unknown as WalletClient<Transport, Chain, Account>

const hash = await remoteWalletClient.sendTransaction({
to: transaction.to as Address | undefined,
//@ts-expect-error
value: transaction.value ? BigInt(transaction.value) : 0n,
chain: null
})
//@ts-expect-error
return {
from: walletClient.account.address,
from: ica.address,
hash
}
}
} as Pick<Wallet, "sendTransaction">
} as Pick<Wallet, 'sendTransaction'>

return mockWallet as Wallet;
return mockWallet as Wallet
}

signTransaction(transaction: providers.TransactionRequest): Promise<string> {
throw new Error("Unsupported signTransaction()")
throw new Error('Unsupported signTransaction()')
}
}
}

122 changes: 63 additions & 59 deletions packages/react-wallet-v2/src/utils/EIP155WalletUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,79 +15,83 @@ let address2: string
* Utilities
*/
export function createOrRestoreEIP155Wallet() {
const mnemonic1 = localStorage.getItem('EIP155_MNEMONIC_1')
const mnemonic2 = localStorage.getItem('EIP155_MNEMONIC_2')
const mnemonic1 = localStorage.getItem('EIP155_MNEMONIC_1')
const mnemonic2 = localStorage.getItem('EIP155_MNEMONIC_2')

if (mnemonic1 && mnemonic2) {
wallet1 = EIP155ViemLib.init({ mnemonicArg: mnemonic1 })
wallet2 = EIP155ViemLib.init({ mnemonicArg: mnemonic2 })
} else {
wallet1 = EIP155ViemLib.init({})
wallet2 = EIP155ViemLib.init({})
if (mnemonic1 && mnemonic2) {
wallet1 = EIP155ViemLib.init({ mnemonicArg: mnemonic1 })
wallet2 = EIP155ViemLib.init({ mnemonicArg: mnemonic2 })
} else {
wallet1 = EIP155ViemLib.init({})
wallet2 = EIP155ViemLib.init({})

// Don't store mnemonic in local storage in a production project!
localStorage.setItem('EIP155_MNEMONIC_1', wallet1.getMnemonic())
localStorage.setItem('EIP155_MNEMONIC_2', wallet2.getMnemonic())
}
// Don't store mnemonic in local storage in a production project!
localStorage.setItem('EIP155_MNEMONIC_1', wallet1.getMnemonic())
localStorage.setItem('EIP155_MNEMONIC_2', wallet2.getMnemonic())
}

address1 = wallet1.getAddress()
address2 = wallet2.getAddress()
address1 = wallet1.getAddress()
address2 = wallet2.getAddress()

eip155Wallets = {
[address1]: wallet1,
[address2]: wallet2
}
eip155Addresses = Object.keys(eip155Wallets)
eip155Wallets = {
[address1]: wallet1,
[address2]: wallet2
}
eip155Addresses = Object.keys(eip155Wallets)

return {
eip155Wallets,
eip155Addresses
}
return {
eip155Wallets,
eip155Addresses
}
}

//TODO: Get wallet, EOA returns EIP155Lib which is similar to a viem account with various functions like getAddress, signTransaction etc...
/**
* Get wallet for the address in params
*/
export const getWallet = async (params: any) => {
const eoaWallet = eip155Wallets[getWalletAddressFromParams(eip155Addresses, params)]
// const eoaWallet = eip155Wallets[getWalletAddressFromParams(eip155Addresses, params)]
//
// if (eoaWallet) {
// return eoaWallet
// }

if (eoaWallet) {
return eoaWallet
}
const chainId = params?.chainId?.split(':')[1]

/**
* Smart accounts
*/
const chainId = params?.chainId?.split(':')[1]
console.log('Chain ID', { chainId })
console.log('PARAMS', { params })
const address = eip155Wallets[0].getAddress()

const address = getWalletAddressFromParams(
Object.keys(smartAccountWallets)
.filter(key => {
const parts = key.split(':')
return parts[0] === chainId
})
.map(key => {
return key.split(':')[1]
}),
params
)
if (!address) {
console.log('Library not initialized for requested address', {
address,
values: Object.keys(smartAccountWallets)
return eip155Wallets[0]
/**
* Smart accounts
*/
console.log('Chain ID', { chainId })
console.log('PARAMS', { params })

const address = getWalletAddressFromParams(
Object.keys(smartAccountWallets)
.filter(key => {
const parts = key.split(':')
return parts[0] === chainId
})
.map(key => {
return key.split(':')[1]
}),
params
)
if (!address) {
console.log('Library not initialized for requested address', {
address,
values: Object.keys(smartAccountWallets)
})
throw new Error('Library not initialized for requested address')
}
const lib = smartAccountWallets[`${chainId}:${address}`]
if (lib) {
return lib
}
console.log('Library not found', {
target: `${chainId}:address`,
values: Object.keys(smartAccountWallets)
})
throw new Error('Library not initialized for requested address')
}
const lib = smartAccountWallets[`${chainId}:${address}`]
if (lib) {
return lib
}
console.log('Library not found', {
target: `${chainId}:address`,
values: Object.keys(smartAccountWallets)
})
throw new Error('Cannot find wallet for requested address')
throw new Error('Cannot find wallet for requested address')
}

0 comments on commit 1ebb4d8

Please sign in to comment.