Skip to content

Commit

Permalink
Merge pull request #317 from focustree/add-multicall-cli
Browse files Browse the repository at this point in the history
Add multicall cli
  • Loading branch information
gabsn authored Dec 17, 2023
2 parents 37744c3 + 7f30b4c commit da0959a
Show file tree
Hide file tree
Showing 19 changed files with 237 additions and 50 deletions.
11 changes: 11 additions & 0 deletions .env.constants → .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
alias sn="dart run examples/starknet_cli/bin/sn.dart"

export PATH=$PATH:$(pwd)/scripts

export STARKNET_RPC=
export STARKNET_CHAIN_ID=

export STARKNET_ACCOUNT_ADDRESS=
export STARKNET_PUBLIC_KEY=
export STARKNET_PRIVATE_KEY=

export BALANCE_CONTRACT_ADDRESS="0xfd89395647bf9792262adc5867d4ee51fa59395d7ddeb760e89cbf075dd74c"
export BALANCE_CLASS_HASH="0x032cad3a2cef95e11de7d8f6b2f1c4f3d9eeda111b9b1b0a2c476ed2565bfff5"

Expand Down
14 changes: 3 additions & 11 deletions .env.katana
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
unset STARKNET_NETWORK
unset STARKNET_KEYSTORE
unset STARKNET_PRIVATE_KEY

source .env.constants
source .env

export STARKNET_RPC="http://0.0.0.0:5050/rpc"
export STARKNET_CHAIN_ID="KATANA"
export STARKNET_ACCOUNT=".starkli/katana-oz-0.json"

export STARKNET_ACCOUNT_ADDRESS="0x517ececd29116499f4a1b64b094da79ba08dfd54a3edaa316134c41f8160973"
# export STARKNET_ACCOUNT_ADDRESS="0x57cfe0bf8eeda17596d4528f6c2e5c17705c2fcfafd5182dbb0f41d3c256b3"
export STARKNET_PUBLIC_KEY="0x2b191c2f3ecf685a91af7cf72a43e7b90e2e41220175de5c4f7498981b10053"
export STARKNET_PRIVATE_KEY="0x1800000000300000180000000000030000000000003006001800006600"
export PATH=$PATH:$(pwd)/scripts

env | grep "STARKNET"
export STARKNET_PRIVATE_KEY="0x1800000000300000180000000000030000000000003006001800006600"
5 changes: 2 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,12 @@ coverage/

.idea

.env

# Python
**/__pycache__
.venv

# Cairo
!contracts/build

.env.testnet
.env.*
!.env.katana
2 changes: 2 additions & 0 deletions assets/test.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
0x04bcb72b74c4f6ed1c406051f6d12e107d1a9ed578656123f46c227a35800b4c,0.0000049
0x048daae66604c3443419a27dde758ade1f702386ed720d2084c4d08cecefad51,0.0000049
10 changes: 10 additions & 0 deletions assets/winners-starkpunks.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
0x04bcb72b74c4f6ed1c406051f6d12e107d1a9ed578656123f46c227a35800b4c,0.049
0x048daae66604c3443419a27dde758ade1f702386ed720d2084c4d08cecefad51,0.049
0x043ec97b2307d69ae5818e9cfc746e408a354b26bbf67bdcd1bdfd78504d41a6,0.049
0x04f1a5324037ec83694f2ef3c64dce3a645b4dd3a16c9e7110d2af911e3c26c5,0.049
0x03c3d383c4e78e6554a0a363b043617cff6d063c56bfbf590c8973d9e47950d1,0.049
0x06c5eca298d24a604194a5e88e31bf24c3b96247944da52d8e0e158e833578d6,0.049
0x01dc978fa72bd8203c15a09ce4afc64e6a5b9b3b36d536a67a36d412b0f3de87,0.049
0x075056821111e82d10abc409449958b05043a04a706a4e84f5d9f9b37e46e9a7,0.049
0x00999458e4037369a84a48f0a33359cb4f1d9aef7f136632440512d173201a4d,0.049
0x0752d8740eb587d498c6249acf238fe9b31bfd8d9f6671240660c54aaf60693b,0.049
30 changes: 30 additions & 0 deletions assets/winners.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
0x04bcb72b74c4f6ed1c406051f6d12e107d1a9ed578656123f46c227a35800b4c,0.049
0x048daae66604c3443419a27dde758ade1f702386ed720d2084c4d08cecefad51,0.049
0x043ec97b2307d69ae5818e9cfc746e408a354b26bbf67bdcd1bdfd78504d41a6,0.049
0x04f1a5324037ec83694f2ef3c64dce3a645b4dd3a16c9e7110d2af911e3c26c5,0.049
0x03c3d383c4e78e6554a0a363b043617cff6d063c56bfbf590c8973d9e47950d1,0.049
0x06c5eca298d24a604194a5e88e31bf24c3b96247944da52d8e0e158e833578d6,0.049
0x01dc978fa72bd8203c15a09ce4afc64e6a5b9b3b36d536a67a36d412b0f3de87,0.049
0x075056821111e82d10abc409449958b05043a04a706a4e84f5d9f9b37e46e9a7,0.049
0x00999458e4037369a84a48f0a33359cb4f1d9aef7f136632440512d173201a4d,0.049
0x0752d8740eb587d498c6249acf238fe9b31bfd8d9f6671240660c54aaf60693b,0.049
0x05caae0e7c8b9cbed02e31c7886e5966fbd3e6ea58e560e00732e31df4eb0fe8,0.024
0x0749296ca55fea77f8fd89d827f71e8f569cfebd38d7d9539e25ca5d8d929394,0.024
0x0719a7886c4a27cd167419b68eeb81ec2ce0d0688270c3c143040b344bcd482b,0.024
0x00decddcefa730dd657333f3c9103bfd22db6137de2f1ba2883ad78286240773,0.024
0x01dceb9b9eddcb98e2d1090023e12e22baf48887c8696e6494ffed6bc25bec22,0.024
0x0630905d2a6865d1b94cc930137232df629ebcceb31b77d044ef66aa6858606e,0.024
0x070370be7692a10b303a70caad9edd11b50d99586cff720fc4845e7f83f13c3d,0.024
0x0400ffbb161a82396b55d7bf954402640ecd62673bb1f05e69a7b186e9342f90,0.024
0x058a838fc9ae21570751317e4b798767f1ede84f8f7ca2d45bcef3d0440bb0a6,0.024
0x06994e832150f6c61e7e5791f48cd8c50cc45d7132f04a2054a27bd510bbe4c7,0.024
0x07890d57be36ec3dd91ca71e1ef91331ed3b1419d2ba3a90a77a9864749acd60,0.012
0x0707a98dcb368a48de073d403aa5c6ac3b60dd8c3e45ee9ab693518818c626ca,0.012
0x07a13630f054bd72b85a21ce9b6929338c59d5945ea014f82c4d2f82f01280d5,0.012
0x05dae5624e16aeef3a877b1b710b5a11f15cb374d8be1e21e7850e549b5744c6,0.012
0x01f796880310808c01c5e55c4c4444f5f5ef4572db9c82af68e64ec70c90f27a,0.012
0x069f51059768c77fe6ccfd954bfe531825f27d5ee7b5649d67df743be5ecef3c,0.012
0x0218bf487f95c38408450914f9e2ec4290fc804de241fe870b9bd04dd4cae411,0.012
0x05489ca2b6292d2c7d0bf2410eb102946cd94711ca67dcecd95ccba404036760,0.012
0x0623a815376bdd496f9c548ebf7d354487ac50aeb33e2f178fececb77e503f79,0.012
0x07a45881d06e74ec8c9a58e31dc1fee61d30959679bf946ac02d0115fcac7661,0.012
2 changes: 2 additions & 0 deletions examples/starknet_cli/bin/sn.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:starknet_cli/block_number.dart';
import 'package:starknet_cli/call.dart';
import 'package:starknet_cli/deploy.dart';
import 'package:starknet_cli/invoke.dart';
import 'package:starknet_cli/signer.dart';

void main(List<String> args) {
CommandRunner("sn", "A Starknet CLI written in Dart")
Expand All @@ -16,6 +17,7 @@ void main(List<String> args) {
..addCommand(DeployCommand())
..addCommand(AccountCommand())
..addCommand(ERC20Command())
..addCommand(SignerCommand())
..argParser.addOption(
"rpc",
help: "Starknet RPC Endpoint",
Expand Down
25 changes: 11 additions & 14 deletions examples/starknet_cli/lib/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,24 @@ class AccountDeployCommand extends Command {
@override
final description = "Deploy pre-funded account contract";

AccountDeployCommand() {
argParser.addOption(
"public-key",
help: "Public Key",
valueHelp: "0x...",
mandatory: true,
);
}

@override
void run() async {
final publicKey = Felt.fromHexString(argResults?['public-key']);
final accountAddress =
OpenzeppelinAccountDerivation().computeAddress(publicKey: publicKey);
final accountClassHash = Felt.fromHexString(
"0x0251cac7b2f45d255b83b7a06dcdef70c8a8752f00ea776517c1c2243c7a06e5",
); // Focus Account
final publicKey = Felt.fromHexString(globalResults?['public-key']);
final privateKey = Felt.fromHexString(globalResults?['private-key']);
final accountAddress = Contract.computeAddress(
classHash: accountClassHash,
calldata: [Signer(privateKey: privateKey).publicKey],
salt: Signer(privateKey: privateKey).publicKey,
);
print("Account address: ${accountAddress.toHexString()}");
final res = await Account.deployAccount(
signer: signerFromArgs(globalResults),
provider: providerFromArgs(globalResults),
constructorCalldata: [publicKey],
classHash: Felt.fromHexString(
"0x016e51dbfd788a497bd54333d0c7c4096a1120770f9fff9a733f51a075446975"),
classHash: accountClassHash,
);

print(res.when(
Expand Down
20 changes: 20 additions & 0 deletions examples/starknet_cli/lib/chain_id.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import 'package:args/command_runner.dart';
import 'package:starknet_cli/utils.dart';

class ChainIdCommand extends Command {
@override
final name = "chain-id";
@override
final description = "Get chain id";

ChainIdCommand();

@override
void run() async {
final provider = providerFromArgs(globalResults);
final chainId = await provider.chainId();
print(chainId.whenOrNull(
result: (result) => result,
));
}
}
95 changes: 94 additions & 1 deletion examples/starknet_cli/lib/erc20.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import 'dart:convert';

import 'package:args/command_runner.dart';
import 'package:starknet/starknet.dart';
import 'package:starknet_cli/utils.dart';
import 'package:starknet_provider/starknet_provider.dart';
import 'dart:io';
import 'package:csv/csv.dart';

const feeContractAddress =
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7";
Expand All @@ -15,6 +19,7 @@ class ERC20Command extends Command {
ERC20Command() {
addSubcommand(ERC20BalanceCommand());
addSubcommand(ERC20SendCommand());
addSubcommand(ERC20MultiSendCommand());
}
}

Expand Down Expand Up @@ -97,7 +102,7 @@ class ERC20SendCommand extends Command {
calldata: [
Felt.fromHexString(argResults?['to']),
Felt.fromIntString(argResults?['amount']),
Felt.fromInt(0)
Felt.fromInt(0) // Uint high
],
)
]);
Expand All @@ -108,3 +113,91 @@ class ERC20SendCommand extends Command {
));
}
}

class ERC20MultiSendCommand extends Command {
@override
final name = "multi-send";
@override
final description = "Send ERC20 tokens to multiple addresses from a CSV file";

ERC20MultiSendCommand() {
argParser.addOption(
"contract-address",
abbr: "a",
help: "ERC20 contract address",
valueHelp: "0x...",
defaultsTo: feeContractAddress,
);
argParser.addOption(
"file",
help: "Path to the CSV file (should contain 2 columns: address, amount))",
valueHelp: "path/to/file.csv",
mandatory: true,
);
}

@override
void run() async {
final account = accountFromArgs(globalResults);
final contractAddress = Felt.fromHexString(argResults?['contract-address']);
final filePath = argResults?['file'];

// Read and parse the CSV file
final input = File(filePath).openRead();
final fields = await input
.transform(utf8.decoder)
.transform(CsvToListConverter())
.toList();

// Create FunctionCall list from CSV data
List<FunctionCall> transfers = [];
for (var i = 0; i < fields.length; i++) {
final row = fields[i];

// Check if the row has exactly 2 columns
if (row.length != 2) {
throw FormatException(
'Invalid format in CSV file at line ${i + 1}: Expected 2 columns, found ${row.length}.');
}

final String address = row[0];
final double amountInEth = row[1];
final amountInWei = ethToWei(amountInEth);

// Validate address and amount formats (implement isValidAddress and isValidAmount according to your criteria)
if (!isValidAddress(address)) {
throw FormatException('Invalid data format at line ${i + 1}.');
}

transfers.add(FunctionCall(
contractAddress: contractAddress,
entryPointSelector: getSelectorByName("transfer"),
calldata: [
Felt.fromHexString(address),
Felt(amountInWei),
Felt.fromInt(0)
],
));
}

print(transfers);

// Execute the multi-call transaction
final res = await account.execute(functionCalls: transfers);

print(res.when(
result: (result) => result,
error: (error) => throw Exception(error),
));
}
}

bool isValidAddress(String address) {
// Check if the address starts with '0x' and has a total length of 66 characters
return address.startsWith('0x') && address.length == 66;
}

BigInt ethToWei(double ethAmount) {
// Multiply by 10^18 to convert to wei
return BigInt.from(ethAmount * 1e18);
}
31 changes: 31 additions & 0 deletions examples/starknet_cli/lib/signer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import 'package:args/command_runner.dart';
import 'package:starknet/starknet.dart';

class SignerCommand extends Command {
@override
final name = 'signer';
@override
final description = 'Signer related commands';

SignerCommand() {
addSubcommand(SignerGetPublicKeyCommand());
}
}

class SignerGetPublicKeyCommand extends Command {
@override
final name = 'get-public-key';
@override
final description = 'Get public key from a private key';

@override
void run() {
try {
final privateKey = Felt.fromHexString(globalResults?['private-key']);
final publicKey = Signer(privateKey: privateKey).publicKey;
print('Public Key: ${publicKey.toHexString()}');
} catch (e) {
print('Error: ${e.toString()}');
}
}
}
3 changes: 2 additions & 1 deletion examples/starknet_cli/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ description: A sample command-line application.
version: 1.0.0

environment:
sdk: ^3.0.5
sdk: ^3.0.0

dependencies:
starknet:
starknet_provider:
args: ^2.4.2
csv: ^5.1.1

dev_dependencies:
lints: ^2.0.0
Expand Down
2 changes: 0 additions & 2 deletions examples/starknet_counter/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),

home: const CounterPage(title: 'Flutter Starknet'),
);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Future<int> getCurrentCount() async {
);
}

Future<String>increaseCounter() async {
Future<String> increaseCounter() async {
print('print increment');
final response = await signeraccount.execute(functionCalls: [
FunctionCall(
Expand Down
6 changes: 2 additions & 4 deletions examples/starknet_counter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: counter
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
publish_to: "none" # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
Expand All @@ -19,8 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1

environment:
sdk: '>=3.1.2 <4.0.0'

sdk: ">=3.0.0 <4.0.0"

dependencies:
flutter:
Expand All @@ -41,7 +40,6 @@ dev_dependencies:
sdk: flutter

flutter:

# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
Expand Down
Loading

0 comments on commit da0959a

Please sign in to comment.