Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

viem adapter #27

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,61 @@ Try it in jsfiddle:

https://jsfiddle.net/wuminzhe/gwp4ovz1/4/

## Browser - viem

```html
Collators Amount: <span id="result"></span>

<script type="module">
import {
ethers
} from "https://esm.sh/[email protected]";
import {
custom,
createPublicClient,
createWalletClient
} from "https://esm.sh/[email protected]"
import {
clientBuilder,
publicClientToProvider,
walletClientToSigner
} from "https://esm.sh/[email protected]";

///////////////////////
// storage
///////////////////////
// convert viem public client to ethers provider
const publicClient = createPublicClient({
transport: custom(window.ethereum)
})
const provider = await publicClientToProvider(publicClient);
//
const client = clientBuilder.buildPangolinClient(provider);
const result = await client.storages.darwiniaStaking.collatorCount();
const resultEl = document.getElementById("result");
resultEl.innerHTML = result;

///////////////////////
// call
///////////////////////
// convert viem wallet client to ethers provider & signer
const walletClient = createWalletClient({
transport: custom(window.ethereum)
})
const [provider2, signer] = walletClientToSigner(walletClient)
//
const pangolin = clientBuilder.buildPangolinClient(provider2);
await pangolin.calls.session.setKeys(
signer, {
aura: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
}, // keys
"0x" // proof
);
</script>
```

https://jsfiddle.net/wuminzhe/amwL4jpk/1/

## Usage

### fetch storage
Expand Down
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./src/storage";
export * from "./src/call";
export * from "./src/derived";
export * from "./src/pangolin-tools";
export * from "./src/viem";
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "darwinia.js",
"version": "3.0.2",
"version": "3.0.3",
"description": "A library to help fetch darwinia storages and dispatch calls",
"main": "dist/index.js",
"scripts": {
Expand Down Expand Up @@ -40,10 +40,11 @@
"nodemon": "^2.0.20",
"ts-jest": "^29.0.5",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"
"typescript": "^5.1.6"
},
"dependencies": {
"@polkadot/types": "^9.14.2",
"ethers": "^5.7.2"
"ethers": "^5.7.2",
"viem": "^1.7.0"
}
}
235 changes: 235 additions & 0 deletions src/viem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import { ethers } from "ethers"
import { PublicClient, WalletClient, HttpTransport, defineChain } from "viem"

const darwinia = defineChain({
id: 46,
name: 'Darwinia',
network: 'Darwinia',
nativeCurrency: {
decimals: 18,
name: 'RING',
symbol: 'RING',
},
rpcUrls: {
default: {
http: ['https://rpc.darwinia.network'],
webSocket: ['wss://rpc.darwinia.network'],
},
public: {
http: ['https://rpc.darwinia.network'],
webSocket: ['wss://rpc.darwinia.network'],
}
},
blockExplorers: {
default: { name: 'Explorer', url: 'https://darwinia.subscan.io/' },
}
})

const crab = defineChain({
id: 44,
name: 'Crab',
network: 'Crab',
nativeCurrency: {
decimals: 18,
name: 'CRAB',
symbol: 'CRAB',
},
rpcUrls: {
default: {
http: ['https://crab-rpc.darwinia.network'],
webSocket: ['wss://crab-rpc.darwinia.network'],
},
public: {
http: ['https://crab-rpc.darwinia.network'],
webSocket: ['wss://crab-rpc.darwinia.network'],
}
},
blockExplorers: {
default: { name: 'Explorer', url: 'https://crab.subscan.io/' },
}
})

const pangolin = defineChain({
id: 43,
name: 'Pangolin',
network: 'Pangolin',
nativeCurrency: {
decimals: 18,
name: 'PRING',
symbol: 'PRING',
},
rpcUrls: {
default: {
http: ['https://pangolin-rpc.darwinia.network'],
webSocket: ['wss://pangolin-rpc.darwinia.network'],
},
public: {
http: ['https://pangolin-rpc.darwinia.network'],
webSocket: ['wss://pangolin-rpc.darwinia.network'],
}
},
blockExplorers: {
default: { name: 'Explorer', url: 'https://pangolin.subscan.io/' },
}
})

const pangoro = defineChain({
id: 45,
name: 'Pangoro',
network: 'Pangoro',
nativeCurrency: {
decimals: 18,
name: 'ORING',
symbol: 'ORING',
},
rpcUrls: {
default: {
http: ['https://pangoro-rpc.darwinia.network'],
webSocket: ['wss://pangoro-rpc.darwinia.network'],
},
public: {
http: ['https://pangoro-rpc.darwinia.network'],
webSocket: ['wss://pangoro-rpc.darwinia.network'],
}
},
blockExplorers: {
default: { name: 'Explorer', url: 'https://pangoro.subscan.io/' },
}
})

function getChainByChainId(chainId: number) {
switch (chainId) {
case 43:
return pangolin
case 46:
return darwinia
default:
throw new Error("not support")
}
}

////////////////////////////
// exports
////////////////////////////
export const darwiniaChains = { darwinia, crab, pangolin, pangoro }

export async function publicClientToProvider(publicClient: PublicClient): Promise<ethers.providers.BaseProvider> {
let { chain, transport } = publicClient

// check if the chain id matches
const chainIdFromTransport = await publicClient.getChainId()
if (chain && chain.id != chainIdFromTransport) {
throw new Error(`chain id not match, chain: ${chain.id}, transport: ${chainIdFromTransport}`)
}

chain = getChainByChainId(chainIdFromTransport)
// only support darwinia chain as only the darwinia chains have the dispatch precompile.
if (!chain) throw new Error(`wrong darwinia chain id: ${chainIdFromTransport}`)

const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}

if (transport.type === 'fallback')
return new ethers.providers.FallbackProvider(
(transport.transports as ReturnType<HttpTransport>[]).map(
({ value }) => new ethers.providers.JsonRpcProvider(value?.url, network),
),
)

// check if there is a window ethereum object exists
try {
const windowEthereum = eval("window.ethereum")
return new ethers.providers.Web3Provider(windowEthereum, network)
} catch (err) {
const url = transport.url || chain.rpcUrls?.default?.http[0]
if (url) {
return new ethers.providers.JsonRpcProvider(url, network)
} else {
throw new Error(`not support, transport: ${JSON.stringify(transport)}`)
}
}
}

export async function walletClientToSigner(walletClient: WalletClient, privateKey?: `0x${string}`): Promise<[ethers.providers.BaseProvider, ethers.Signer]> {
let { account, chain, transport } = walletClient

// check if the chain id matches
const chainIdFromTransport = await walletClient.getChainId()
if (chain && chain.id != chainIdFromTransport) {
throw new Error(`chain id not match, chain: ${chain.id}, transport: ${chainIdFromTransport}`)
}

chain = getChainByChainId(chainIdFromTransport)
// only support darwinia chain as only the darwinia chains have the dispatch precompile.
if (!chain) throw new Error(`wrong darwinia chain id: ${chainIdFromTransport}`)

const network = {
chainId: chain.id,
name: chain.name,
ensAddress: chain.contracts?.ensRegistry?.address,
}

// check if there is a window ethereum object exists
try {
const windowEthereum = eval("window.ethereum")
const provider = new ethers.providers.Web3Provider(windowEthereum, network)
let signer: ethers.Signer;
if (account) {
signer = provider.getSigner(account.address)
} else {
signer = provider.getSigner()
}
return [provider, signer]
} catch (err) {
const url = transport.url || chain.rpcUrls?.default?.http[0]
if (url && privateKey) {
const provider = new ethers.providers.JsonRpcProvider(url, network)
const signer = new ethers.Wallet(privateKey, provider);
return [provider, signer]
} else {
throw new Error(`url or privateKey not found, transport: ${JSON.stringify(transport)}`)
}
}
}


// import { http, createPublicClient, createWalletClient } from "viem"
// import { clientBuilder } from "../index"
//
// async function getStorage() {
// const publicClient = createPublicClient({
// transport: http("https://pangolin-rpc.darwinia.network")
// })
//
// const provider = await publicClientToProvider(publicClient)
// const pangolin = clientBuilder.buildPangolinClient(provider);
// const result = await pangolin.storages.darwiniaStaking.ledgers("0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac");
// console.log(`decoded: ${result}\n`);
// }
//
// async function dispatchCall() {
// const privateKey = '0xd5dd1909b74029eb3164b10ce84abaf9b0ea379b3ea0d4e2a96241806b8f8175';
// const walletClient = createWalletClient({
// transport: http("https://pangolin-rpc.darwinia.network")
// })
//
// const [provider, signer] = await walletClientToSigner(walletClient, privateKey)
//
// const pangolin = clientBuilder.buildPangolinClient(provider);
// return await pangolin.calls.session.setKeys(
// signer,
// {
// aura: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
// }, // keys
// "0x" // proof
// );
// }
//
// async function main(): Promise<void> {
// await getStorage()
// console.log(await dispatchCall())
// }
// main().catch(err => console.log(err));