Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@ src/vendor
src/dist
config.js
blockchain/EVM/build
blockchain/EVM/artifacts
blockchain/EVM/cache
blockchain/EVM/ignition/deployments
blockchain/solana/test/data
tmp/
34 changes: 34 additions & 0 deletions blockchain/EVM/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require('@nomicfoundation/hardhat-ethers');
require('@nomicfoundation/hardhat-ignition-ethers');

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: {
compilers: [
{ version: '0.4.24' },
// add more here
]
},
ignition: {
requiredConfirmations: 1
},
networks: {
geth: {
url: `http://geth:8545`,
gas: 4700000,
chainId: 1337
},
local: {
url: process.env.HARDHAT_URL || 'http://localhost:8545',
gas: 4700000,
chainId: 1337
}
},
paths: {
sources: './contracts',
tests: './test',
cache: './cache',
artifacts: './artifacts',
modules: './ignition/modules'
}
};
29 changes: 29 additions & 0 deletions blockchain/EVM/ignition/modules/all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const hardhat = require('hardhat');
const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules');
const fs = require('fs');

const sequential = process.env.HH_SEQUENTIAL_DEPLOY == '1' || hardhat.network.config['sequentialDeploy'];
if (sequential) {
// eslint-disable-next-line no-console
console.log('Sequential deployment enabled');
}


module.exports = buildModule('Deploy_All', (m) => {
const mods = fs.readdirSync(__dirname);
let prev;
for (const mod of mods) {
if (mod === __filename.split('/').pop()) {
continue;
}
const contracts = m.useModule(require(`${__dirname}/${mod}`));
if (sequential) { // sequentially deploy the contracts instead of in parallel
if (prev) {
for (const contract of Object.values(contracts)) {
contract.dependencies.add(prev);
}
}
prev = Object.values(contracts)[0];
}
}
});
7 changes: 7 additions & 0 deletions blockchain/EVM/ignition/modules/erc20.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules');


module.exports = buildModule('ERC20', (m) => {
const contract = m.contract('CryptoErc20');
return { contract };
});
7 changes: 7 additions & 0 deletions blockchain/EVM/ignition/modules/sendToMany.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const { buildModule } = require('@nomicfoundation/hardhat-ignition/modules');


module.exports = buildModule('SendToMany', (m) => {
const contract = m.contract('SendToMany');
return { contract };
});
78 changes: 47 additions & 31 deletions blockchain/EVM/test/sendToMany.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,64 @@
const SendToMany = artifacts.require('SendToMany');
const CryptoErc20 = artifacts.require('CryptoErc20');
const hardhat = require('hardhat');
const assert = require('assert');
const ZERO_ADDR = '0x0000000000000000000000000000000000000000';
contract('SendToMany', (accounts) => {
it('should exist', async() => {
const batcher = await SendToMany.deployed();
assert(batcher);

describe('SendToMany', () => {
let CryptoErc20;
let SendToMany;
let accounts;
let initialEthBalances = [];
let initialErc20Balances = [];

before(async function() {
accounts = (await hardhat.ethers.getSigners()).map(m => m.address);
CryptoErc20 = await hardhat.ethers.deployContract('CryptoErc20');
SendToMany = await hardhat.ethers.deployContract('SendToMany');
for (const account of accounts) {
initialEthBalances.push(await hardhat.ethers.provider.getBalance(account));
initialErc20Balances.push(await CryptoErc20.balanceOf(account));
}
});

it('should send ether', async() => {
const batcher = await SendToMany.deployed();
const receivers = accounts.slice(1);
const amounts = new Array(receivers.length).fill(1e18.toString());
const balanceBefore = await web3.eth.getBalance(accounts[0]);;
console.log('Token balance before', balanceBefore.toString());
const balanceBefore = await hardhat.ethers.provider.getBalance(accounts[0]);
// console.log('ETH balance before', balanceBefore.toString());
const sum = (1e18*receivers.length).toString();
await batcher.sendMany(receivers, amounts, ZERO_ADDR, {value: sum});
const balanceAfter = await web3.eth.getBalance(accounts[0]);;
console.log('ETH balance after', balanceAfter.toString());
for(const receiver of receivers) {
const balance = await web3.eth.getBalance(receiver);
console.log('ETH Balance', receiver, ':', balance.toString());
await SendToMany.sendMany(receivers, amounts, ZERO_ADDR, {value: sum});
const balanceAfter = await hardhat.ethers.provider.getBalance(accounts[0]);
// console.log('ETH balance after', balanceAfter.toString());
assert.ok(balanceBefore - balanceAfter > sum, 'Diff should be >sum because of gas');
for (let i = 0; i < receivers.length; i++) {
const receiver = receivers[i];
const sentAmount = amounts[i];
const initBalance = initialEthBalances[i+1];
const nowBalance = await hardhat.ethers.provider.getBalance(receiver);
const receiptAmt = nowBalance - initBalance;
// console.log('ETH Balance', receiver, ':', nowBalance.toString());
assert.strictEqual(receiptAmt.toString(), sentAmount.toString(), `Balance mismatch: ${i} - ${receiptAmt.toString()} != ${sentAmount.toString()}`);
}
});

it('should have token it can send', async() => {
const token = await CryptoErc20.deployed();
assert(token);
});

it('should send tokens', async() => {
const batcher = await SendToMany.deployed();
const token = await CryptoErc20.deployed();
const receivers = accounts.slice(1);
const amounts = new Array(receivers.length).fill(1e18.toString());
const sum = (1e18*receivers.length).toString();
const balanceBefore = await token.balanceOf(accounts[0]);
console.log('Token balance before', balanceBefore.toString());
await token.approve(batcher.address, sum);
await batcher.sendMany(receivers, amounts, token.address);
const balanceAfter = await token.balanceOf(accounts[0]);
console.log('Token balance after', balanceAfter.toString());
for(const receiver of receivers) {
const balance = await token.balanceOf(receiver);
console.log('Token Balance', receiver, ':', balance.toString());
const balanceBefore = await CryptoErc20.balanceOf(accounts[0]);
// console.log('Token balance before', balanceBefore.toString());
await CryptoErc20.approve(SendToMany.target, sum);
await SendToMany.sendMany(receivers, amounts, CryptoErc20.target);
const balanceAfter = await CryptoErc20.balanceOf(accounts[0]);
// console.log('Token balance after', balanceAfter.toString());
assert.ok(balanceBefore - balanceAfter == sum, 'Diff should be =sum because gas is paid in ETH');
for (let i = 0; i < receivers.length; i++) {
const receiver = receivers[i];
const sentAmount = amounts[i];
const initBalance = initialErc20Balances[i+1];
const nowBalance = await CryptoErc20.balanceOf(receiver);
const receiptAmt = nowBalance - initBalance;
// console.log('Token Balance', receiver, ':', nowBalance.toString());
assert.strictEqual(receiptAmt.toString(), sentAmount.toString(), `Balance mismatch: ${i} - ${receiptAmt.toString()} != ${sentAmount.toString()}`);
}
});

Expand Down
42 changes: 11 additions & 31 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
version: "3"

services:

start:
build:
context: .
Expand All @@ -21,7 +18,6 @@ services:
- lightning
- lightning2
- geth
- ganache
- solana

test_runner:
Expand All @@ -43,7 +39,6 @@ services:
- lightning
- lightning2
- geth
- ganache
- solana

bitcoin:
Expand Down Expand Up @@ -125,7 +120,7 @@ services:
restart: always

geth:
image: 0labs/geth:v1.10.21
image: ethereum/client-go:v1.14.13
volumes:
- ./tests/docker/geth-keystore:/keystore
ports:
Expand All @@ -134,34 +129,19 @@ services:
default:
ipv4_address: 172.28.0.7
command:
geth
--dev
--datadir=/home/kjoseph/nodes/dev/geth
--networkid=1337
--datadir /geth
--networkid 1337
--http
--http.api=web3,eth,debug,personal,net
--http.corsdomain='*'
--http.vhosts='*'
--http.addr=0.0.0.0
--http.port=8545
--keystore=/keystore
--http.api web3,eth,debug,net
--http.corsdomain '*'
--http.vhosts '*'
--http.addr 0.0.0.0
--http.port 8545
--keystore /keystore
--allow-insecure-unlock
--unlock=00a329c0648769a73afac7f9381e08fb43dbea72
--password=/keystore/pw

ganache:
image: trufflesuite/ganache-cli:v6.12.2
ports:
- "10545:8545"
networks:
default:
ipv4_address: 172.28.0.11
command:
-m "dose youth patient boring disagree tuna random tower tornado version violin around"
-b 2
-g 20000000000
-p 8545
-a 20
--unlock 00a329c0648769a73afac7f9381e08fb43dbea72
--password /keystore/pw

rippled:
networks:
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module.exports = {
BalanceProgram: require('./bin/balance'),
SendProgram: require('./bin/send'),
CryptoRpc: require('./lib')
CryptoRpc: require('./lib'),
utils: require('./lib/utils')
};
53 changes: 26 additions & 27 deletions lib/erc20/Erc20Rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,43 @@ class Erc20RPC extends EthRPC {
);
}

#getWallet(account) {
return this.web3.eth.accounts.wallet.get(account);
}

// this will only work on ERC20 tokens with decimals
async sendToAddress({ address, amount, fromAccount, passphrase, gasPrice, nonce, gas }) {
async sendToAddress({ address, amount, fromAccount, gasPrice, nonce, gas }) {
if (!gasPrice) {
gasPrice = await this.estimateGasPrice();
}
const account = fromAccount || await this.getAccount();
const account = fromAccount || this.getAccount();
const amountStr = Number(amount).toLocaleString('fullwide', { useGrouping: false });
const contractData = this.erc20Contract.methods
.transfer(address, amountStr)
.encodeABI();

if (passphrase) {
this.emitter.emit('unlockedForOne');
const wallet = this.#getWallet(account);
if (!wallet) {
throw new Error('Account not found. Make sure you add it first with addAccount()');
}

let result;

try {
result = await this.web3.eth.personal.sendTransaction(
{
from: account,
gasPrice,
data: contractData,
to: this.tokenContractAddress,
nonce,
gas
},
passphrase
const signed = await wallet.signTransaction({
from: account,
gasPrice,
data: contractData,
to: this.tokenContractAddress,
nonce,
gas
});
const txid = await new Promise((resolve, reject) =>
this.web3.eth.sendSignedTransaction(signed.rawTransaction).on('transactionHash', resolve).on('error', reject)
);

if (passphrase) {
this.emitter.emit('locked');
}
return txid;
} catch (error) {
this.emitter.emit('locked');
throw new Error(error);
}

return result;
}

async getBalance({ address }) {
Expand All @@ -59,11 +58,11 @@ class Erc20RPC extends EthRPC {
.call();
return balance;
} else {
const accounts = await this.web3.eth.getAccounts();
const wallets = await this.web3.eth.accounts.wallet;
const balances = [];
for (let account of accounts) {
const balance = await this.getBalance({ address: account });
balances.push({ account, balance });
for (const wallet of wallets) {
const balance = await this.getBalance({ address: wallet.address });
balances.push({ account: wallet.address, balance });
}
return balances;
}
Expand All @@ -73,7 +72,7 @@ class Erc20RPC extends EthRPC {
const decodedEthTx = await super.decodeRawTransaction({ rawTx });
if (decodedEthTx.data) {
try {
const erc20Interface = new ethers.utils.Interface(erc20);
const erc20Interface = new ethers.Interface(erc20);
decodedEthTx.decodedData = await erc20Interface.parseTransaction({ data: decodedEthTx.data });
} catch (err) {
decodedEthTx.decodedData = undefined;
Expand Down
Loading