Skip to content

Commit

Permalink
Merge pull request #8 from nabla-studio/DavideSegullo/feat-signing
Browse files Browse the repository at this point in the history
Davide segullo/feat signing
  • Loading branch information
DavideSegullo authored Nov 5, 2023
2 parents 6c16720 + e9198c5 commit b92a3be
Show file tree
Hide file tree
Showing 45 changed files with 1,209 additions and 96 deletions.
6 changes: 2 additions & 4 deletions examples/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { osmosis, osmosisAssetList } from '@nabla-studio/chain-registry';
import { TestChain } from '../components/test-chain';
import { Button } from '../components/button';
import { Test } from '../components/test';

export default async function Index() {
return (
<div>
{osmosis.chain_name}
{osmosisAssetList.chain_name}

<Test />
<TestChain />
<Button />
</div>
);
Expand Down
14 changes: 9 additions & 5 deletions examples/nextjs/components/button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use client';

import { bitsong, bitsongAssetList } from '@nabla-studio/chain-registry';
import { useConfig, useConnect } from '@quirks/react';
import { suggestChains } from '@quirks/store';

export const Button = () => {
const { wallets } = useConfig();
Expand All @@ -13,16 +15,18 @@ export const Button = () => {
return wallets.map((wallet) => (
<div key={wallet.options.name}>
<button
onClick={() => {
connect(wallet.options.name);
onClick={async () => {
await suggestChains(wallet.options.name, [
{ chain: bitsong, assetList: bitsongAssetList, name: 'bitsong' },
]);
await connect(wallet.options.name);
}}
>
<img
src={wallet.options.logoUrls?.light?.svg}
alt={wallet.options.prettyName}
style={{
maxWidth: 128,
}}
height="48px"
width="48px"
/>
</button>

Expand Down
13 changes: 9 additions & 4 deletions examples/nextjs/components/provider.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
'use client';

import { osmosis, osmosisAssetList } from '@nabla-studio/chain-registry';
import {
bitsong,
bitsongAssetList,
osmosis,
osmosisAssetList,
} from '@nabla-studio/chain-registry';
import { QuirksConfig, QuirksNextProvider } from '@quirks/react';
import { Config, ssrPersistOptions } from '@quirks/store';
import { type Config, ssrPersistOptions } from '@quirks/store';
import { keplrExtension, leapExtension } from '@quirks/wallets';
import { PropsWithChildren } from 'react';

const config: Config = {
wallets: [keplrExtension, leapExtension],
chains: [osmosis],
assetsLists: [osmosisAssetList],
chains: [osmosis, bitsong],
assetsLists: [osmosisAssetList, bitsongAssetList],
persistOptions: ssrPersistOptions,
};

Expand Down
22 changes: 22 additions & 0 deletions examples/nextjs/components/test-chain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';

import { useChain, useConnect } from '@quirks/react';

export const TestChain = () => {
const { connected } = useConnect();
const { address, chain } = useChain('osmosis');

if (!connected) {
return false;
}

return (
<div>
<div>Chain ID: {chain.chain_id}</div>

<div>Chain Name: Osmosis</div>

<div>Address: {address}</div>
</div>
);
};
31 changes: 31 additions & 0 deletions examples/nextjs/components/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,36 @@

import { useChains, useConnect } from '@quirks/react';

const send = async () => {
const cosmos = (await import('osmojs')).cosmos;
const sign = (await import('@quirks/store')).sign;
const getAddress = (await import('@quirks/store')).getAddress;
const { send } = cosmos.bank.v1beta1.MessageComposer.withTypeUrl;

const address = getAddress('osmosis');

const msg = send({
amount: [
{
denom: 'uosmo',
amount: '1',
},
],
toAddress: address,
fromAddress: address,
});

console.log(msg);

const txRaw = await sign('osmosis', [msg]);

const broadcast = (await import('@quirks/store')).broadcast;

const res = await broadcast('osmosis', txRaw);

console.log(res);
};

export const Test = () => {
const { status, connected } = useConnect();
const { accounts } = useChains();
Expand All @@ -12,6 +42,7 @@ export const Test = () => {
{connected ? (
<div>
Addresses:
<button onClick={send}>SIGN</button>
{accounts.map((account) => (
<div key={account.chainId}>
<div>Chain ID: {account.chainId}</div>
Expand Down
7 changes: 7 additions & 0 deletions examples/nextjs/etc/noop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Noop

This is copied as a design pattern from Keplr-wallet!

https://github.com/chainapsis/keplr-wallet/tree/v0.12.25/etc/noop

> This directory isn’t actually used, and would be preferred to not be added. But it is needed to explicitly ignore specific libraries used by the dependency. Specifically, libsodium isn’t actually used and WebAssembly can’t be used in the extension’s sandbox environment but creates various errors so the directory exists to ignore libsodium.
1 change: 1 addition & 0 deletions examples/nextjs/etc/noop/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
8 changes: 8 additions & 0 deletions examples/nextjs/etc/noop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "noop",
"version": "0.0.1",
"main": "index.js",
"private": true,
"scripts": {},
"dependencies": {}
}
21 changes: 21 additions & 0 deletions examples/nextjs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');
const { resolve } = require('path');

/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
Expand All @@ -12,6 +13,26 @@ const nextConfig = {
// See: https://github.com/gregberge/svgr
svgr: false,
},
webpack: (config) => {
const noop = resolve(__dirname, 'etc', 'noop', 'index.js');

config.resolve = {
...config.resolve, // This spreads existing resolve configuration (if any)
alias: {
...config.resolve.alias, // This spreads any existing alias configurations
libsodium: noop,
'libsodium-wrappers': noop,
'libsodium-sumo': noop,
'libsodium-wrappers-sumo': noop,
// bip39 is only used in the context of the extension wallet, so we can replace it.
// replacing it with a no-op breaks build, so we can at least replace it with a lighter weight version for now.
// ideally this becomes replaced with an API-compatible no-op.
bip39: noop,
},
};

return config;
},
};

const plugins = [
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,20 @@
},
"dependencies": {
"@cosmjs/amino": "^0.31.3",
"@cosmjs/cosmwasm-stargate": "^0.31.3",
"@cosmjs/proto-signing": "^0.31.3",
"@cosmjs/stargate": "^0.31.3",
"@keplr-wallet/cosmos": "^0.12.38",
"bufferutil": "^4.0.8",
"cosmjs-types": "^0.9.0",
"eventemitter3": "^5.0.1",
"long": "^5.2.3",
"next": "13.5.6",
"osmojs": "^16.5.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"semver": "^7.5.4",
"utf-8-validate": "^6.0.3",
"zustand": "^4.4.4"
}
}
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@nabla-studio/chain-registry": "*"
},
"peerDependencies": {
"@cosmjs/stargate": "^0.31.3",
"@cosmjs/proto-signing": "^0.31.3",
"cosmjs-types": "^0.9.0",
"@cosmjs/amino": "^0.31.3",
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/types/fees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { EncodeObject } from '@cosmjs/proto-signing';

export interface SigningSimulatorClient {
simulate(
signerAddress: string,
messages: readonly EncodeObject[],
memo: string | undefined,
): Promise<number>;
}
1 change: 1 addition & 0 deletions packages/core/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './suggest-tokens';
export * from './suggest-chains';
export * from './wallet';
export * from './account';
export * from './fees';
18 changes: 18 additions & 0 deletions packages/core/src/utils/endpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Chain } from '@nabla-studio/chain-registry';
import { assertIsDefined } from './asserts';

export const getEndpoint = (chainName: string, chains: Chain[]) => {
const chain = chains.find((el) => el.chain_name === chainName);

assertIsDefined(chain);
assertIsDefined(chain.apis?.rpc);
assertIsDefined(chain.apis?.rest);

const rpc = chain.apis.rpc[0];
const rest = chain.apis.rest[0];

return {
rpc,
rest,
};
};
57 changes: 57 additions & 0 deletions packages/core/src/utils/fees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { EncodeObject } from '@cosmjs/proto-signing';
import { calculateFee, GasPrice } from '@cosmjs/stargate';
import { assertIsDefined } from './asserts';
import type { SigningSimulatorClient } from '../types';
import type { Chain } from '@nabla-studio/chain-registry';

/**
* Retrieve chain gas price so we can use fee auto.
*
* @param chain
* @param feeDenom ex. uosmo
* @returns
*/
export const getGasPrice = (chain: Chain, feeDenom?: string) => {
let gasPrice: GasPrice | undefined = undefined;

if (chain.fees && chain.fees.fee_tokens.length > 0) {
let feeToken = undefined;

if (feeToken) {
feeToken = chain.fees.fee_tokens.find(
(token) => token.denom === feeDenom,
);
} else {
chain.fees.fee_tokens[0];
}

const averageGasPrice = feeToken?.average_gas_price;
const denom = feeToken?.denom;

if (averageGasPrice && denom && !denom.startsWith('ibc/')) {
gasPrice = GasPrice.fromString(`${averageGasPrice}${denom}`);
} else {
gasPrice = GasPrice.fromString(`1${denom}`);
}
}

return gasPrice;
};

export const estimateFee = async (
client: SigningSimulatorClient,
sender: string,
messages: EncodeObject[],
gasPrice?: string | GasPrice,
memo?: string,
multiplier = 1.4,
) => {
assertIsDefined(
gasPrice,
'Gas price must be set in the client options when auto gas is used.',
);

const gasEstimation = await client.simulate(sender, messages, memo);

return calculateFee(Math.round(gasEstimation * multiplier), gasPrice);
};
2 changes: 2 additions & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './extension';
export * from './asserts';
export * from './errors';
export * from './fees';
export * from './endpoints';
14 changes: 14 additions & 0 deletions packages/core/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
AminoSignResponse,
OfflineAminoSigner,
StdSignDoc,
StdSignature,
} from '@cosmjs/amino';
import type {
SignOptions,
Expand Down Expand Up @@ -90,6 +91,19 @@ export abstract class Wallet<T = unknown> {
signOptions?: SignOptions,
): Promise<DirectSignResponse>;

abstract signArbitrary(
chainId: string,
signer: string,
data: string | Uint8Array,
): Promise<StdSignature>;

abstract verifyArbitrary(
chainId: string,
signer: string,
data: string | Uint8Array,
signature: StdSignature,
): Promise<boolean>;

/**
* Asks the user to add a tokens to the wallet
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/core/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ export default defineConfig({
// External packages that should not be bundled into your library.
external: [
'cosmjs-types',
'cosmjs-types/cosmos/tx/v1beta1/tx',
'@cosmjs/proto-signing',
'@cosmjs/amino',
'@cosmjs/stargate',
'eventemitter3',
'@nabla-studio/chain-registry',
],
},
},
Expand Down
16 changes: 7 additions & 9 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@
"version": "0.2.0",
"sideEffects": false,
"dependencies": {
"@quirks/store": "*"
"@quirks/store": "*",
"@quirks/core": "*"
},
"peerDependencies": {
"react": "18.2.0"
"react": "18.2.0",
"cosmjs-types": "^0.9.0",
"@cosmjs/amino": "^0.31.3"
},
"main": "./index.js",
"types": "./index.d.ts",
"exports": {
".": {
"import": "./index.mjs",
"require": "./index.js"
}
}
"module": "./index.mjs",
"typings": "./index.d.ts"
}
Loading

0 comments on commit b92a3be

Please sign in to comment.