Skip to content

Commit

Permalink
Merge branch 'main' into v1.0.0-rc.13
Browse files Browse the repository at this point in the history
  • Loading branch information
mocolicious authored Aug 11, 2024
2 parents a2e951c + 8403837 commit 6e6cf0b
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 90 deletions.
51 changes: 51 additions & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Flutter Package

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
publish:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: 3.22.2

- name: Install dependencies
run: flutter pub get

# - name: Run tests
# run: flutter test

- name: Create credentials.json
env:
PUBDEV_TOKEN: ${{ secrets.PUBDEV_TOKEN }}
run: |
# Set the configuration path
CONFIG_PATH="${XDG_CONFIG_HOME:-$HOME/.config}/dart"
# Ensure the directory exists
mkdir -p "$CONFIG_PATH"
# Write the credentials to the appropriate location
echo "$PUBDEV_TOKEN" > "$CONFIG_PATH/pub-credentials.json"
# Verify the file was created correctly
cat "$CONFIG_PATH/pub-credentials.json"
- name: Publish to pub.dev (dry run)
run: flutter pub publish --dry-run

- name: Publish to pub.dev
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: flutter pub publish --force
54 changes: 0 additions & 54 deletions .github/workflows/flutter-test.yml

This file was deleted.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ setup:
npm install @openapitools/openapi-generator-cli -g

generate_api:
npx openapi-generator-cli generate -i https://raw.githubusercontent.com/kin-labs/kinetic/main/api-swagger.json -g dart -o lib/generated
npx openapi-generator-cli generate -i https://raw.githubusercontent.com/kin-labs/kinetic/dev/api-swagger.json -g dart -o lib/generated

generate_pre:
rm -rf lib/generated
Expand Down
15 changes: 10 additions & 5 deletions lib/generated/lib/api/account_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,16 @@ class AccountApi {
///
/// * [String] accountId (required):
///
/// * [String] mint (required):
///
/// * [Commitment] commitment (required):
Future<Response> getAccountInfoWithHttpInfo(String environment, int index, String accountId, Commitment commitment,) async {
Future<Response> getAccountInfoWithHttpInfo(String environment, int index, String accountId, String mint, Commitment commitment,) async {
// ignore: prefer_const_declarations
final path = r'/api/account/info/{environment}/{index}/{accountId}'
final path = r'/api/account/info/{environment}/{index}/{accountId}/{mint}'
.replaceAll('{environment}', environment)
.replaceAll('{index}', index.toString())
.replaceAll('{accountId}', accountId);
.replaceAll('{accountId}', accountId)
.replaceAll('{mint}', mint);

// ignore: prefer_final_locals
Object? postBody;
Expand Down Expand Up @@ -173,9 +176,11 @@ class AccountApi {
///
/// * [String] accountId (required):
///
/// * [String] mint (required):
///
/// * [Commitment] commitment (required):
Future<AccountInfo?> getAccountInfo(String environment, int index, String accountId, Commitment commitment,) async {
final response = await getAccountInfoWithHttpInfo(environment, index, accountId, commitment,);
Future<AccountInfo?> getAccountInfo(String environment, int index, String accountId, String mint, Commitment commitment,) async {
final response = await getAccountInfoWithHttpInfo(environment, index, accountId, mint, commitment,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
Expand Down
26 changes: 13 additions & 13 deletions lib/helpers/generate_make_transfer_transaction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,52 @@ import 'package:kinetic_sdk/tools.dart';
import 'package:solana/encoder.dart';
import 'package:solana/solana.dart';

Future<SignedTx> generateMakeTransferTransaction(GenerateMakeTransferOptions options, {List fk = const []}) async {
Future<SignedTx> generateMakeTransferTransaction(GenerateMakeTransferOptions options) async {
// Create objects from Response
final destinationPublicKey = getPublicKey(options.destination);
final destinationTokenAccountPublicKey = getPublicKey(options.destinationTokenAccount);
final feePayerKey = getPublicKey(options.mintFeePayer);
final mintKey = getPublicKey(options.mintPublicKey);

final destinationPublicKey = getPublicKey(options.destination);
final ownerPublicKey = options.owner.solanaPublicKey;

// Get TokenAccount from Owner and Destination
final destinationTokenAccount = await findAssociatedTokenAddress(mint: mintKey, owner: destinationPublicKey);
final ownerTokenAccount = await findAssociatedTokenAddress(mint: mintKey, owner: ownerPublicKey);
final ownerTokenAccountPublicKey = getPublicKey(options.ownerTokenAccount);

// Create Instructions
List<Instruction> instructions = [];

// Create the Memo Instruction
if (options.addMemo) {
var memo = createKinMemoInstruction(options.type, options.index);
instructions.add(MemoInstruction(signers: [], memo: base64Encode(memo)));
}

// Create the Token Account if senderCreate is enabled
if (options.senderCreate != null && options.senderCreate!) {
instructions.add(AssociatedTokenAccountInstruction.createAccount(
address: destinationTokenAccount,
address: destinationTokenAccountPublicKey,
funder: feePayerKey,
mint: mintKey,
owner: destinationPublicKey,
));
}

List<Ed25519HDPublicKey> signersPublic = [ownerPublicKey, feePayerKey];

// Create the Token Transfer Instruction
instructions.add(TokenInstruction.transferChecked(
decimals: options.mintDecimals,
mint: mintKey,
source: ownerTokenAccount,
destination: destinationTokenAccount,
source: ownerTokenAccountPublicKey,
destination: destinationTokenAccountPublicKey,
owner: ownerPublicKey,
amount: getRawQuantity(double.parse(options.amount), options.mintDecimals).toInt(),
signers: signersPublic,
signers: [ownerPublicKey, feePayerKey],
));

// Create transaction
final CompiledMessage message = Message(instructions: instructions).compile(
recentBlockhash: options.blockhash,
feePayer: feePayerKey,
);

// Partially sign the transaction
return SignedTx(
signatures: [
Signature(List.filled(64, 0), publicKey: feePayerKey),
Expand Down
12 changes: 12 additions & 0 deletions lib/helpers/get_token_address.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:kinetic/helpers/get_public_key.dart';
import 'package:solana/solana.dart';

// Function with 2 params
Future<String> getTokenAddress({
required String account,
required String mint,
}) async {
final address = await findAssociatedTokenAddress(mint: getPublicKey(mint), owner: getPublicKey(account));

return address.toString();
}
4 changes: 4 additions & 0 deletions lib/interfaces/generate_make_transfer_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ class GenerateMakeTransferOptions {
required this.amount,
required this.blockhash,
required this.destination,
required this.destinationTokenAccount,
required this.index,
required this.lastValidBlockHeight,
required this.mintDecimals,
required this.mintFeePayer,
required this.mintPublicKey,
required this.owner,
required this.ownerTokenAccount,
required this.type,
this.senderCreate,
});
Expand All @@ -21,12 +23,14 @@ class GenerateMakeTransferOptions {
late String amount;
late String blockhash;
late String destination;
late String destinationTokenAccount;
late int index;
late int lastValidBlockHeight;
late int mintDecimals;
late String mintFeePayer;
late String mintPublicKey;
late Keypair owner;
late String ownerTokenAccount;
late bool? senderCreate;
late TransactionType type;
}
2 changes: 2 additions & 0 deletions lib/interfaces/get_account_info_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ class GetAccountInfoOptions {
GetAccountInfoOptions({
required this.account,
this.commitment,
this.mint,
});

late String account;
late Commitment? commitment;
late String? mint;
}
5 changes: 5 additions & 0 deletions lib/interfaces/prepare_transaction_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class PrepareTransactionResponse {
final String blockhash;
final int lastValidBlockHeight;
PrepareTransactionResponse({required this.blockhash, required this.lastValidBlockHeight});
}
66 changes: 53 additions & 13 deletions lib/kinetic_sdk_internal.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,15 @@ class KineticSdkInternal {
}

Future<AccountInfo?> getAccountInfo(GetAccountInfoOptions options) async {
var appConfig = _ensureAppConfig();
Commitment commitment = _getCommitment(options.commitment);
AppConfigMint? mint = _getAppMint(appConfig, options.mint);

return accountApi.getAccountInfo(
sdkConfig.environment,
sdkConfig.index,
options.account,
mint.publicKey,
commitment,
);
}
Expand Down Expand Up @@ -163,21 +166,45 @@ class KineticSdkInternal {

Future<Transaction?> makeTransfer(MakeTransferOptions options) async {
var appConfig = _ensureAppConfig();
Commitment commitment = _getCommitment(options.commitment);
AppConfigMint? mint = _getAppMint(appConfig, options.mint);

var commitment = options.commitment ?? Commitment.confirmed;
var destination = options.destination;
var senderCreate = options.senderCreate ?? false;

_validateDestination(appConfig, destination);
// We get the token account for the owner
var ownerTokenAccount = await _findTokenAccount(
account: options.owner.publicKey,
commitment: commitment,
mint: mint.publicKey,
);

// The operation fails if the owner doesn't have a token account for this mint
if (ownerTokenAccount == null) {
throw Exception("Owner account doesn't exist for mint ${mint.publicKey}.");
}

List<String>? accounts = await getTokenAccounts(GetTokenAccountsOptions(
account: options.destination,
// We get the account info for the destination
var destinationTokenAccount = await _findTokenAccount(
account: destination,
commitment: commitment,
mint: mint.publicKey,
));
);

if (!senderCreate && (accounts == null || accounts.isEmpty)) {
throw Exception("Destination account does not exist");
// The operation fails if the destination doesn't have a token account for this mint and senderCreate is not set
if (destinationTokenAccount == null && !senderCreate) {
throw Exception("Destination account doesn't exist for mint ${mint.publicKey}.");
}

// Derive the associated token address if the destination doesn't have a token account for this mint and senderCreate is set
String? senderCreateTokenAccount;
if (destinationTokenAccount == null && senderCreate) {
senderCreateTokenAccount = await getTokenAddress(account: destination, mint: mint.publicKey);
}

// The operation fails if there is still no destination token account
if (destinationTokenAccount == null && senderCreateTokenAccount == null) {
throw Exception("Destination account not found.");
}

PrepareTransactionResponse blockhash = await _getBlockhash();
Expand All @@ -187,12 +214,14 @@ class KineticSdkInternal {
amount: options.amount,
blockhash: blockhash.blockhash,
destination: options.destination,
destinationTokenAccount: (destinationTokenAccount ?? senderCreateTokenAccount)!,
index: sdkConfig.index,
lastValidBlockHeight: blockhash.lastValidBlockHeight,
mintDecimals: mint.decimals,
mintFeePayer: mint.feePayer,
mintPublicKey: mint.publicKey,
owner: options.owner,
ownerTokenAccount: ownerTokenAccount,
senderCreate: options.senderCreate,
type: options.type ?? TransactionType.none,
));
Expand Down Expand Up @@ -233,6 +262,23 @@ class KineticSdkInternal {
return appConfig!;
}

Future<String?> _findTokenAccount(
{required String account, required Commitment commitment, required String mint}) async {
// We get the account info for the account
var accountInfo = await getAccountInfo(GetAccountInfoOptions(
account: account,
commitment: commitment,
mint: mint,
));
// The operation fails when the account is a mint account
if (accountInfo != null && accountInfo.isMint) {
throw Exception("Account is a mint account.");
}
// Find the token account for this mint
// FIXME: we need to support the use case where the account has multiple accounts for this mint
return accountInfo?.tokens?.firstWhere((element) => element.mint == mint).account;
}

AppConfigMint _getAppMint(AppConfig appConfig, String? mint) {
mint = mint ?? appConfig.mint.publicKey;
final AppConfigMint? found = appConfig.mints.firstWhere((element) => element.publicKey == mint);
Expand Down Expand Up @@ -265,9 +311,3 @@ class KineticSdkInternal {
}
}
}

class PrepareTransactionResponse {
final String blockhash;
final int lastValidBlockHeight;
PrepareTransactionResponse({required this.blockhash, required this.lastValidBlockHeight});
}
Loading

0 comments on commit 6e6cf0b

Please sign in to comment.