Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor Batch Enhancements #368

Merged
merged 10 commits into from
May 2, 2022
2 changes: 1 addition & 1 deletion SharedBuildProperties.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>Solnet</Product>
<Version>6.0.6</Version>
<Version>6.0.7</Version>
<Copyright>Copyright 2022 &#169; Solnet</Copyright>
<Authors>blockmountain</Authors>
<PublisherName>blockmountain</PublisherName>
Expand Down
11 changes: 11 additions & 0 deletions src/Solnet.Extensions/Models/TokenWallet/TokenWalletFilterList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ public TokenWalletFilterList WithMint(string mint)
return new TokenWalletFilterList(_list.Where(x => x.TokenMint == mint));
}

/// <summary>
/// Uses the TokenDef TokenMint to keep all matching accounts.
/// </summary>
/// <param name="tokenDef">A TokenDef instance.</param>
/// <returns>A filtered list of accounts for the given mint.</returns>
public TokenWalletFilterList WithMint(TokenDef tokenDef)
{
if (tokenDef == null) throw new ArgumentNullException(nameof(tokenDef));
return WithMint(tokenDef.TokenMint);
}

/// <summary>
/// Keeps all accounts with at least the supplied minimum balance.
/// </summary>
Expand Down
112 changes: 112 additions & 0 deletions src/Solnet.Extensions/TokenWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ private TokenWallet(ITokenWalletRpcProxy client, ITokenMintResolver mintResolver
_ataCache = new Dictionary<string, PublicKey>();
}

/// <summary>
/// Private constructor, get your instances via Load methods
/// </summary>
private TokenWallet(ITokenMintResolver mintResolver, PublicKey publicKey)
{
if (mintResolver is null) throw new ArgumentNullException(nameof(mintResolver));
if (publicKey is null) throw new ArgumentNullException(nameof(publicKey));
MintResolver = mintResolver;
PublicKey = publicKey;
_ataCache = new Dictionary<string, PublicKey>();
}

#region Overloaded Load methods

/// <summary>
Expand Down Expand Up @@ -181,6 +193,106 @@ public async static Task<TokenWallet> LoadAsync(ITokenWalletRpcProxy client,
return output;
}


/// <summary>
/// Creates and loads a TokenWallet instance using an existing RPC batch call.
/// </summary>
/// <param name="batch">An instance of SolanaRpcBatchWithCallbacks</param>
/// <param name="mintResolver">An instance of a mint resolver.</param>
/// <param name="publicKey">The account public key.</param>
/// <param name="commitment">The state commitment to consider when querying the ledger state.</param>
/// <returns>A TokenWallet task that will trigger once the batch has executed.</returns>
public static Task<TokenWallet> LoadAsync(SolanaRpcBatchWithCallbacks batch,
ITokenMintResolver mintResolver,
PublicKey publicKey,
Commitment commitment = Commitment.Finalized)
{
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));
if (!publicKey.IsOnCurve()) throw new ArgumentException("PublicKey not valid - check this is native wallet address (not an ATA, PDA or aux account)");
return LoadAsync(batch, mintResolver, publicKey.Key, commitment);
}

/// <summary>
/// Creates and loads a TokenWallet instance using an existing RPC batch call.
/// </summary>
/// <param name="batch">An instance of SolanaRpcBatchWithCallbacks</param>
/// <param name="mintResolver">An instance of a mint resolver.</param>
/// <param name="publicKey">The account public key.</param>
/// <param name="commitment">The state commitment to consider when querying the ledger state.</param>
/// <returns>A TokenWallet task that will trigger once the batch has executed.</returns>
public static Task<TokenWallet> LoadAsync(SolanaRpcBatchWithCallbacks batch,
ITokenMintResolver mintResolver,
string publicKey,
Commitment commitment = Commitment.Finalized)
{
if (batch == null) throw new ArgumentNullException(nameof(batch));
if (mintResolver == null) throw new ArgumentNullException(nameof(mintResolver));
if (publicKey == null) throw new ArgumentNullException(nameof(publicKey));

// create the task source
var taskSource = new TaskCompletionSource<TokenWallet>();
var success = 0;
var fail = 0;
ulong lamports = 0;
List<TokenAccount> tokenAccounts = null;

// function to create a token wallet when both callbacks have responsed (in any order)
Action<Exception> wrapUp = ex =>
{
if (success == 2)
{
var tokenWallet = new TokenWallet(mintResolver, new PublicKey(publicKey));
tokenWallet.Lamports = lamports;
tokenWallet._tokenAccounts = tokenAccounts;
taskSource.SetResult(tokenWallet);
}
else if (fail + success == 2)
taskSource.SetException(new ApplicationException("Failed to load TokenWallet via Batch"));
};

// get sol balance
batch.GetBalance(publicKey, commitment, callback: (x, ex) =>
{
// handle balance response
lock (taskSource)
{
if (x != null)
{
lamports = x.Value;
success += 1;
}
else
fail += 1;
}

// finished?
wrapUp.Invoke(ex);
});

// load token accounts
batch.GetTokenAccountsByOwner(publicKey, null, TokenProgram.ProgramIdKey, commitment, callback: (x, ex) =>
{
// handle token list response
lock (taskSource)
{
if (x != null)
{
tokenAccounts = x.Value;
success += 1;
}
else
fail += 1;
}

// finished?
wrapUp.Invoke(ex);
});

liquizard marked this conversation as resolved.
Show resolved Hide resolved
// return the task
return taskSource.Task;

}

#endregion

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion src/Solnet.Rpc/SolanaRpcBatchWithCallbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public void AutoExecute(BatchAutoExecuteMode mode, int batchSizeTrigger)
/// </summary>
public void Flush()
{
_composer.Flush();
if (_composer.Count > 0)
_composer.Flush();
}

#region RPC Methods
Expand Down
5 changes: 4 additions & 1 deletion src/Solnet.Rpc/Solnet.Rpc.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Solnet.Rpc.Test</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Solnet.Extensions.Test</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>DynamicProxyGenAssembly2</_Parameter1>
</AssemblyAttribute>
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"method":"getBalance","params":["9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5"],"jsonrpc":"2.0","id":0},{"method":"getTokenAccountsByOwner","params":["9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",{"programId":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"encoding":"jsonParsed"}],"jsonrpc":"2.0","id":1}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
[
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 79274779
},
"value": 168855000000
},
"id": 0
},
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 79200468
},
"value": [
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "V15kW7xzaTJRDbdbC35GYT6QUcBtDzwmhrnGZDRtR2j",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "7WU3jHeeJh4sHUkVBpMrirpm2c518j61uhKSEimSM7WW"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "G5SNrU9nCx65WUcEksc7ue63Eib5wNyVZivVvZrDi8ZU",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "AZDRzWyQ4yeUWaNHx9BcEj3h92K1rwtk8FeacHEgjsL5"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "98mCaWvZYTmTHmimisaAQW4WGLphN1cWhcC7KtnZF819",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "G5SA5eMmbqSFnNZNB2fQV9ipHbh9y9KS65aZkAh9t8zv"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "88ocFjrLgHEMQRMwozC7NnDBQUsq2UoQaqREFZoDEex",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "4NSREK36nAr32vooa3L9z8tu6JWj5rY3k4KnsqTgynvm"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "6rmHU2X6nau25MdDgYpg53SXNuQ6BAPnsxSGraf3D4qM",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 196
},
"pubkey": "gazDxCc7qX3RR4EiY7Y3ByBV5G6KTdELwusoJXnMZ3d"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "GXEbZguUSni5at9MQkEC4k9Q7iXzzfWtLsZfKWeUFeMW",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "4YqDGVsBzHfnyGweeVoYhyxvdcrYUBx8kwnfDohnAAyj"
},
{
"account": {
"data": {
"parsed": {
"info": {
"isNative": false,
"mint": "D1NFXiUz8CmFaF9raPt57rMh9xYQp16zuNcRzBcBcUiq",
"owner": "9we6kjtbcZ2vy3GSLLsZTEhbAqXPTRvEyoxa8wxSqKp5",
"state": "initialized",
"tokenAmount": {
"amount": "1000",
"decimals": 2,
"uiAmount": 10.0,
"uiAmountString": "10"
}
},
"type": "account"
},
"program": "spl-token",
"space": 165
},
"executable": false,
"lamports": 2039280,
"owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"rentEpoch": 195
},
"pubkey": "CFAibeX7kA5QaGLfFHN93bMD1uap9nL7B7UvPrzaZVdZ"
}
]
},
"id": 0
}
]
Loading