aptos 0.0.2 copy "aptos: ^0.0.2" to clipboard
aptos: ^0.0.2 copied to clipboard

Aptos Dart SDK.

example/lib/main.dart

import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';

import 'package:aptos/aptos.dart';
import 'package:aptos/coin_client.dart';
import 'package:aptos/constants.dart';
import 'package:aptos/models/payload.dart';
import 'package:aptos/models/signature.dart';
import 'package:aptos/models/transaction.dart';
import 'package:flutter/material.dart';
import 'package:ed25519_edwards/ed25519_edwards.dart' as ed25519;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Aptos',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Transfer Aptos'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final privateKeyTextEditingController = TextEditingController();
  final addressTextEditingController = TextEditingController();
  final amountTextEditingController = TextEditingController();
  var message = "";

  Future<dynamic> _transferWithEncodeSubmissionAPI(
      String privateKey,
      String receiverAddress,
      BigInt amount,
      BigInt gasPrice,
      BigInt maxGasAmount,
      BigInt expirationTimestamp) async {

    final aptos = AptosClient(Constants.testnetAPI, enableDebugLog: true);
    final account = AptosAccount(Uint8List.fromList(HEX.decode(privateKey)));
    final sender = account.address;
    final accountInfo = await aptos.getAccount(sender);
    final txSubmission = TransactionEncodeSubmissionRequest(
      sender: sender,
      sequenceNumber: accountInfo.sequenceNumber,
      maxGasAmount: maxGasAmount.toString(),
      gasUnitPrice: gasPrice.toString(),
      expirationTimestampSecs: expirationTimestamp.toString(),
      payload: Payload(
          "entry_function_payload",
          "0x1::coin::transfer",
          ["0x1::aptos_coin::AptosCoin"],
          [receiverAddress, amount.toString()]
      )
    );

    final encodeTx = await aptos.encodeSubmission(txSubmission);
    final hex = HEX.decode(encodeTx.substring(2));
    final hexBytes = Uint8List.fromList(hex);
    final privateKeyBytes = HexString(privateKey).toUint8Array();
    final signingKey = ed25519.PrivateKey(privateKeyBytes);
    final signEncode = ed25519.sign(signingKey, hexBytes).sublist(0, 64);

    final tx = TransactionRequest(
        sender: txSubmission.sender,
        sequenceNumber: txSubmission.sequenceNumber,
        maxGasAmount: txSubmission.maxGasAmount.toString(),
        gasUnitPrice: txSubmission.gasUnitPrice.toString(),
        expirationTimestampSecs: txSubmission.expirationTimestampSecs.toString(),
        payload: txSubmission.payload,
        signature: Signature("ed25519_signature", account.pubKey().hex(), "0x"+HEX.encode(signEncode)));

    final result = await aptos.submitTransaction(tx);
    debugPrint(result.toString());
    return result;
  }

  Future<dynamic> _transfer(String privateKey,
      String receiverAddress,
      BigInt amount,
      BigInt gasPrice,
      BigInt maxGasAmount,
      BigInt expirationTimestamp) async {
    final aptos = AptosClient(Constants.testnetAPI, enableDebugLog: true);
    final account = AptosAccount(Uint8List.fromList(HEX.decode(privateKey)));
    final sender = account.address;

    final accountInfo = await aptos.getAccount(sender);
    final ledgerInfo = await aptos.getLedgerInfo();
    final sequenceNumber = int.parse(accountInfo.sequenceNumber);

    const typeArgs = "0x1::aptos_coin::AptosCoin";
    const moduleId = "0x1::coin";
    const moduleFunc = "transfer";
    final entryFunc = EntryFunction.natural(
      moduleId,
      moduleFunc,
      [TypeTagStruct(StructTag.fromString(typeArgs))],
      [bcsToBytes(AccountAddress.fromHex(receiverAddress)), bcsSerializeUint64(amount)],
    );
    final entryFunctionPayload = TransactionPayloadEntryFunction(entryFunc);

    final rawTx = RawTransaction(
        AccountAddress.fromHex(sender),
        BigInt.from(sequenceNumber),
        entryFunctionPayload,
        maxGasAmount,
        gasPrice,
        expirationTimestamp,
        ChainId(ledgerInfo["chain_id"]));

    final privateKeyBytes = HexString(privateKey).toUint8Array();
    final signingKey = ed25519.newKeyFromSeed(privateKeyBytes.sublist(0, 32));
    final publicKey = ed25519.public(signingKey);

    final txnBuilder = TransactionBuilderEd25519(
      Uint8List.fromList(publicKey.bytes),
          (signingMessage) => Ed25519Signature(ed25519.sign(signingKey, signingMessage).sublist(0, 64)),
    );

    final signedTx = txnBuilder.rawToSigned(rawTx);
    final txEdd25519 = signedTx.authenticator as TransactionAuthenticatorEd25519;
    final signature = txEdd25519.signature.value;

    final tx = TransactionRequest(
        sender: sender,
        sequenceNumber: sequenceNumber.toString(),
        maxGasAmount: maxGasAmount.toString(),
        gasUnitPrice: gasPrice.toString(),
        expirationTimestampSecs: expirationTimestamp.toString(),
        payload: Payload(
            "entry_function_payload",
            "$moduleId::$moduleFunc",
            ["0x1::aptos_coin::AptosCoin"],
            [receiverAddress, amount.toString()]),
        signature: Signature("ed25519_signature", account.pubKey().hex(), "0x"+HEX.encode(signature)));

    final result = await aptos.submitTransaction(tx);
    debugPrint(result.toString());
    return result;
  }

  Future<dynamic> _transferAptos(
      AptosAccount account,
      String receiverAddress,
      BigInt amount,
      {BigInt? gasPrice,
      BigInt? maxGasAmount,
      BigInt? expirationTimestamp}) async {
    final aptos = AptosClient(Constants.testnetAPI, enableDebugLog: true);
    final coinClient = CoinClient(aptos);
    final txHash = await coinClient.transfer(
      account,
      receiverAddress,
      amount,
      maxGasAmount: maxGasAmount,
      gasUnitPrice: gasPrice,
      expireTimestamp: expirationTimestamp
    );
    debugPrint(txHash);
    return txHash;
  }


  void _send() async {
    final privateKey = privateKeyTextEditingController.text.trim();
    final address = addressTextEditingController.text.trim();
    final amountText = amountTextEditingController.text.trim();

    AptosAccount account;
    try {
      account = AptosAccount(HexString(privateKey).toUint8Array());
    } catch (e) {
      setState(() {
        message = "invalid private key length";
      });
      return;
    }

    final amount = double.tryParse(amountText);
    if (amount == null) {
      setState(() {
        message = "invalid aptos amount";
      });
      return;
    }

    final totalAptos = BigInt.from(amount * pow(10, 8));
    final result = await _transferAptos(account, address, totalAptos);
    setState(() {
      message = "txhash: ${result["hash"]}";
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Container(
        padding: const EdgeInsets.symmetric(horizontal: 20),
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextField(
                controller: privateKeyTextEditingController,
                decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Private key"
                )
            ),
            const SizedBox(height: 40),
              TextField(
                controller: addressTextEditingController,
                decoration: const InputDecoration(
                  border: OutlineInputBorder(),
                  labelText: "Receiver address"
                )
              ),
            const SizedBox(height: 20),
            TextField(
              controller: amountTextEditingController,
                decoration: const InputDecoration(
                    border: OutlineInputBorder(),
                    labelText: "Aptos amount"
                )
            ),
            const SizedBox(height: 20),
            TextButton(
                onPressed: _send,
                child: const Text("Send", style: TextStyle(fontSize: 30))
            ),
            const SizedBox(height: 20),
            Text(message, style: const TextStyle(color: Colors.red))
          ],
        ),
      ),
      )
    );
  }
}