Skip to content

Commit

Permalink
feat(web3.js): web3.js support
Browse files Browse the repository at this point in the history
  • Loading branch information
isaldin committed Apr 30, 2024
1 parent e038042 commit e42659b
Show file tree
Hide file tree
Showing 44 changed files with 2,463 additions and 130 deletions.
6 changes: 5 additions & 1 deletion commitlint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export default {
'test',
],
] as [RuleConfigSeverity, RuleConfigCondition, string[]],
'scope-enum': [RuleConfigSeverity.Error, 'always', ['global', 'core', 'examples', 'ethers']],
'scope-enum': [
RuleConfigSeverity.Error,
'always',
['global', 'core', 'examples', 'ethers', 'web3.js'],
],
},
prompt: {
questions: {
Expand Down
122 changes: 122 additions & 0 deletions examples/nodejs-example/web3-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
Address,
Chain,
CommonBuilderOptions,
InMemoryStorage,
PriceTrigger,
Provider,
SmartWalletFactory,
TimeBasedTrigger,
TimeScale,
tokens,
UniswapSwapActionCallDataBuilder,
WorkflowsFactory,
} from '@ditto-network/core';
import { Web3jsContractFactory, Web3jsSigner } from '@ditto-network/web3.js';
import { Web3 } from 'web3';

(async () => {
const chainId = Chain.Polygon;

const web3 = new Web3(process.env.INFURA_API_URL!);
const accounts = web3.eth.accounts.wallet.add(`0x${process.env.PRIVATE_KEY!}`);
const account = accounts[0].address! as Address;

const txParamsBuildFn = async () => {
const { baseFeePerGas } = await web3.eth.getBlock('pending');

return {
from: account,
maxFeePerGas: `${BigInt(2) * baseFeePerGas!}`,
maxPriorityFeePerGas: `${baseFeePerGas! / BigInt(2)}`,
};
};

const storage = new InMemoryStorage();
const dittoProvider = new Provider({
signer: new Web3jsSigner(web3, account, txParamsBuildFn),
storage,
contractFactory: new Web3jsContractFactory(web3, txParamsBuildFn),
});

const needAuth = await dittoProvider.needAuthentication();
if (needAuth) {
await dittoProvider.authenticate();
}

const accountAddress = await dittoProvider.getSigner().getAddress();

const swFactory = new SmartWalletFactory(dittoProvider);
// const vault = await swFactory.getDefaultOrCreateVault(chainId);
const vaultAddress = '0xb1Ec673122AC9eb2f3efb51c62911D16d3a29919' as Address; // vault.getAddress()!;

const commonConfig = {
chainId,
recipient: vaultAddress,
accountAddress,
vaultAddress,
provider: dittoProvider,
} satisfies CommonBuilderOptions;

const workflowsFactory = new WorkflowsFactory(dittoProvider);

const wmatic = tokens.wrappedNative[Chain.Polygon];
const usdt = tokens.stableCoins[Chain.Polygon].USDT;

const timeTrigger = new TimeBasedTrigger(
{
repeatTimes: 2,
startAtTimestamp: new Date().getTime() / 1000 + 120,
cycle: {
frequency: 1,
scale: TimeScale.Minutes,
},
},
commonConfig
);

// rate = how much of fromToken you should pay to get one toToken
// rate should be:
// a) higher than triggerAtPrice if priceMustBeHigherThan is true
// b) lower than triggerAtPrice if priceMustBeHigherThan is false
// in this case rate is 0.88 (0.88 USDT for 1 WMATIC)
// triggerAtPrice is 0.3 (300000 / 1e6)
// current rate is higher than triggerAtPrice and priceMustBeHigherThan is true so the trigger should be triggered
const priceTrigger = new PriceTrigger(
{
uniswapPoolFeeTier: 3000,
triggerAtPrice: '300000',
priceMustBeHigherThan: true,
fromToken: usdt,
toToken: wmatic,
},
commonConfig
);

const usePriceTrigger = true;

const wf = await workflowsFactory.create({
name: 'My first workflow',
triggers: [usePriceTrigger ? priceTrigger : timeTrigger],
actions: [
new UniswapSwapActionCallDataBuilder(
{
fromToken: wmatic,
toToken: usdt,
fromAmount: `333444555000000`,
slippagePercent: 0.05,
providerStrategy: {
type: 'nodejs',
chainId: chainId,
rpcUrl: process.env.INFURA_API_URL!,
},
},
commonConfig
),
],
chainId,
});

const hash = await wf.buildAndDeploy(commonConfig.vaultAddress, commonConfig.accountAddress);
console.log('Workflow hash:', hash);
})();
2 changes: 1 addition & 1 deletion examples/sandbox/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": ["plugin:@nx/react", "../.eslintrc.json"],
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
Expand Down
34 changes: 2 additions & 32 deletions examples/sandbox/src/app/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ import { Button, Popover, PopoverContent, PopoverTrigger } from '../components/u
import { formatAddress } from '../lib/utils';
import { useSDK, MetaMaskProvider } from '@metamask/sdk-react';
import { Link } from 'react-router-dom';
import {
BrowserStorage,
Chain,
EthersContractFactory,
EthersSigner,
Provider,
SmartWalletFactory,
} from '@ditto-network/core';
import { BrowserStorage, Chain, Provider, SmartWalletFactory } from '@ditto-network/core';
import { EthersContractFactory, EthersSigner } from '@ditto-network/ethers';
import { ethers } from 'ethers';

// https://github.com/Uniswap/smart-order-router/issues/484
Expand Down Expand Up @@ -121,16 +115,6 @@ export function App() {
setAuth(authResult);
};

const handlePredictVaultAddressSDKClick = async () => {
console.log('handlePredictVaultAddressSDKClick');
if (!provider) return;

const smartWalletFactory = new SmartWalletFactory(provider, Chain.Polygon);

const addr = await smartWalletFactory.predictVaultAddress();
setSmartWalletAddress(addr as string);
};

return (
<div className="w-full h-screen">
<NavBar />
Expand Down Expand Up @@ -170,20 +154,6 @@ export function App() {
<div className="flex flex-col gap-2">
<h2 className="text-2xl font-bold">Smart Wallet</h2>
<p className="text-gray-600">Create a smart wallet and deploy it to the blockchain</p>

<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Button className="w-min" onClick={handlePredictVaultAddressSDKClick}>
Predict Vault Address
</Button>
</div>
<p className="text-gray-600">
This will predict the address of the smart wallet based on the wallet address
</p>
<p className="text-gray-600">
Predicted address: {smartWalletAddress ? smartWalletAddress : '❌ Not predicted'}
</p>
</div>
</div>
<div className="flex flex-col gap-2">
<h2 className="text-2xl font-bold">Workflow</h2>
Expand Down
61 changes: 48 additions & 13 deletions examples/sandbox/src/components/contract-testing/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,61 @@
import { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import { BrowserStorage, EthersContractFactory, EthersSigner, Provider } from '@ditto-network/core';
import {
Address,
BrowserStorage,
MutationTransactionReturnType,
Provider,
} from '@ditto-network/core';
import storageAbi from './storage.abi.json';
import { Web3 } from 'web3';
import { Web3jsContractFactory, Web3jsSigner } from '@ditto-network/web3.js';

export const ContractTesting = () => {
const [auth, setAuth] = useState(false);
const [provider, setProvider] = useState<Provider>();
const [hash, setHash] = useState('');
const [value, setValue] = useState<string>('');

useEffect(() => {
const browserProvider = new ethers.BrowserProvider(window.ethereum!);
browserProvider.getSigner().then((signer) => {
const provider = new Provider({
signer: new EthersSigner(signer),
storage: new BrowserStorage(),
contractFactory: new EthersContractFactory(signer),
});
const getProvider = async () => {
const web3 = new Web3(window.ethereum);
await window.ethereum!.request({ method: 'eth_requestAccounts' });
const accounts = await web3.eth.getAccounts();
const account = accounts[0] as Address;

const txParamsBuildFn = async () => {
const { baseFeePerGas } = await web3.eth.getBlock('pending');
return {
from: account,
maxFeePerGas: `${BigInt(2) * baseFeePerGas!}`,
maxPriorityFeePerGas: `${baseFeePerGas! / BigInt(2)}`,
};
};

return new Provider({
signer: new Web3jsSigner(web3, account, txParamsBuildFn),
storage: new BrowserStorage(),
contractFactory: new Web3jsContractFactory(web3, txParamsBuildFn),
});
};

useEffect(() => {
getProvider().then((provider) => {
setProvider(provider);
});
}, []);

// useEffect(() => {
// const browserProvider = new ethers.BrowserProvider(window.ethereum!);
// browserProvider.getSigner().then((signer) => {
// const provider = new Provider({
// signer: new EthersSigner(signer),
// storage: new BrowserStorage(),
// contractFactory: new EthersContractFactory(signer),
// });
//
// setProvider(provider);
// });
// }, []);

const handleSignClick = async () => {
const authResult = await provider!.authenticate();
setAuth(authResult);
Expand All @@ -42,10 +76,11 @@ export const ContractTesting = () => {
.getContractFactory()
.getContract('0xd10e3E8EbC4B55eAE572181be1554356Fb2a7767', JSON.stringify(storageAbi));

// @ts-expect-error cast
const tx = await contract.call<{ hash: string }, string>('store', BigInt(value!));
const tx = await contract.call<MutationTransactionReturnType, [bigint]>('store', [
BigInt(value!),
]);
setHash(`Wait for ${tx.hash} to be mined...`);
// @ts-expect-error hack

await tx.wait();
setValue('');
setHash(`Tx mined! Pls retieve the value`);
Expand Down
3 changes: 2 additions & 1 deletion examples/sandbox/src/components/signer-test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BrowserStorage, EthersContractFactory, EthersSigner, Provider } from '@ditto-network/core';
import { BrowserStorage, Provider } from '@ditto-network/core';
import { EthersContractFactory, EthersSigner } from '@ditto-network/ethers';
import { ethers } from 'ethers';
import { useState } from 'react';

Expand Down
10 changes: 2 additions & 8 deletions examples/sandbox/src/components/vaults.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import React, { useEffect } from 'react';
import { ethers } from 'ethers';
import {
BrowserStorage,
Chain,
EthersContractFactory,
EthersSigner,
Provider,
SmartWalletFactory,
} from '@ditto-network/core';
import { BrowserStorage, Chain, Provider, SmartWalletFactory } from '@ditto-network/core';
import { EthersSigner, EthersContractFactory } from '@ditto-network/ethers';

export const Vaults: React.FC = () => {
const [provider, setProvider] = React.useState<Provider | null>(null);
Expand Down
Loading

0 comments on commit e42659b

Please sign in to comment.