Skip to content

Commit

Permalink
Merge pull request #85 from p2p-org/feature/token-2022
Browse files Browse the repository at this point in the history
Token 2022 Support
  • Loading branch information
bigearsenal authored Jan 25, 2024
2 parents d39aeb8 + 5c3b9b9 commit 48f98d4
Show file tree
Hide file tree
Showing 43 changed files with 2,293 additions and 461 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 5.0.0

- Fix TokenMetadata encoding, tags.
- Remove deprecated PublicKey.tokenProgramId, use TokenProgram.id instead.
- Remove deprecated PublicKey.programId, use SystemProgram.id instead.
- Remove deprecated PublicKey.ownerValidationProgramId, use OwnerValidationProgram.id instead.
- Remove deprecated PublicKey.splAssociatedTokenAccountProgramId, use AssociatedTokenProgram.id instead.
- Remove deprecated typealias AccountInfo, use TokenAccountState or Token2022AccountState instead.
- Remove deprecated typealias Mint, use TokenMintState or Token2022MintState instead.
- Remove deprecated typealias Wallet, use AccountBalance instead.
- Support token 2022 via method getAccountBalances (See GetAccountBalancesTests).
- Support token 2022 and Token2022Program.

## 4.0.0

- Rename Wallet to AccountBalance.
Expand Down
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ let package = Package(
),
.testTarget(
name: "SolanaSwiftUnitTests",
dependencies: ["SolanaSwift"]
dependencies: ["SolanaSwift"],
resources: [
.process("Resources/get_all_tokens_info.json"),
]
),
.testTarget(
name: "SolanaSwiftIntegrationTests",
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ Solana-blockchain client, written in pure swift.
[![Platform](https://img.shields.io/cocoapods/p/SolanaSwift.svg?style=flat)](https://cocoapods.org/pods/SolanaSwift)
[![Documentation Status](https://readthedocs.org/projects/ansicolortags/badge/?version=latest)](https://p2p-org.github.io/solana-swift/documentation/solanaswift)

## Breaking changes
### v5.0
...
- Remove deprecated typealias Mint, use SPLTokenMintState or Token2022MintState instead.
- Remove deprecated typealias Wallet, use AccountBalance instead.
- Support token 2022 via method getAccountBalances (See GetAccountBalancesTests).
- Support token 2022 and Token2022Program.
...
[See more](https://github.com/p2p-org/solana-swift/blob/main/CHANGELOG.md)

## Features
- [x] Supported swift concurrency (from 2.0.0)
- [x] Key pairs generation
Expand Down
52 changes: 37 additions & 15 deletions Sources/SolanaSwift/APIClient/APIClient+Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ import Foundation
public extension SolanaAPIClient {
// MARK: - Convenience methods

func getTokenAccountsByOwner(
pubkey: String,
params: OwnerInfoParams?,
configs: RequestConfiguration?
) async throws -> [TokenAccount<TokenAccountState>] {
try await getTokenAccountsByOwner(
pubkey: pubkey,
params: params,
configs: configs,
decodingTo: TokenAccountState.self
)
}

func getMinimumBalanceForRentExemption(span: UInt64) async throws -> UInt64 {
try await getMinimumBalanceForRentExemption(dataLength: span, commitment: "recent")
}
Expand All @@ -27,19 +40,21 @@ public extension SolanaAPIClient {
try await request(method: method, params: [])
}

func getMultipleMintDatas(
func getMultipleMintDatas<M: MintLayoutState>(
mintAddresses: [String],
commitment: Commitment
) async throws -> [String: SPLTokenMintState?] {
let accounts: [BufferInfo<SPLTokenMintState>?] = try await getMultipleAccounts(
commitment: Commitment,
mintType _: M.Type
) async throws -> [String: M] {
let accounts: [BufferInfo<M>?] = try await getMultipleAccounts(
pubkeys: mintAddresses,
commitment: commitment
)

var mintDict = [String: SPLTokenMintState?]()
var mintDict = [String: M]()

for (index, address) in mintAddresses.enumerated() {
mintDict[address] = accounts[index]?.data
let account = accounts[index] as BufferInfo<M>?
mintDict[address] = account?.data
}

return mintDict
Expand Down Expand Up @@ -73,16 +88,18 @@ public extension SolanaAPIClient {

func checkIfAssociatedTokenAccountExists(
owner: PublicKey,
mint: String
mint: String,
tokenProgramId: PublicKey
) async throws -> Bool {
let mintAddress = try mint.toPublicKey()

let associatedTokenAccount = try PublicKey.associatedTokenAddress(
walletAddress: owner,
tokenMintAddress: mintAddress
tokenMintAddress: mintAddress,
tokenProgramId: tokenProgramId
)

let bufferInfo: BufferInfo<SPLTokenAccountState>? = try await getAccountInfo(account: associatedTokenAccount
let bufferInfo: BufferInfo<TokenAccountState>? = try await getAccountInfo(account: associatedTokenAccount
.base58EncodedString)
return bufferInfo?.data.mint == mintAddress
}
Expand All @@ -99,10 +116,11 @@ public extension SolanaAPIClient {

func findSPLTokenDestinationAddress(
mintAddress: String,
destinationAddress: String
destinationAddress: String,
tokenProgramId: PublicKey
) async throws -> SPLTokenDestinationAddress {
var address: String
var accountInfo: BufferInfo<SPLTokenAccountState>?
var accountInfo: BufferInfo<TokenAccountState>?
do {
accountInfo = try await getAccountInfoThrowable(account: destinationAddress)
let toTokenMint = accountInfo?.data.mint.base58EncodedString
Expand All @@ -116,7 +134,8 @@ public extension SolanaAPIClient {
// create associated token address
address = try PublicKey.associatedTokenAddress(
walletAddress: owner,
tokenMintAddress: tokenMint
tokenMintAddress: tokenMint,
tokenProgramId: tokenProgramId
).base58EncodedString
} else {
throw PublicKeyError.invalidAddress(destinationAddress)
Expand All @@ -127,7 +146,8 @@ public extension SolanaAPIClient {
// create associated token address
address = try PublicKey.associatedTokenAddress(
walletAddress: owner,
tokenMintAddress: tokenMint
tokenMintAddress: tokenMint,
tokenProgramId: tokenProgramId
).base58EncodedString
} catch {
throw error
Expand All @@ -139,7 +159,7 @@ public extension SolanaAPIClient {
var isUnregisteredAsocciatedToken = false
if destinationAddress != toPublicKey.base58EncodedString {
// check if associated address is already registered
let info: BufferInfo<SPLTokenAccountState>?
let info: BufferInfo<TokenAccountState>?
do {
info = try await getAccountInfoThrowable(account: toPublicKey.base58EncodedString)
} catch {
Expand All @@ -148,7 +168,9 @@ public extension SolanaAPIClient {
isUnregisteredAsocciatedToken = true

// if associated token account has been registered
if info?.owner == TokenProgram.id.base58EncodedString, info?.data != nil {
if PublicKey.isSPLTokenProgram(info?.owner),
info?.data != nil
{
isUnregisteredAsocciatedToken = false
}
}
Expand Down
17 changes: 9 additions & 8 deletions Sources/SolanaSwift/APIClient/Networking/JSONRPCAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,13 @@ public class JSONRPCAPIClient: SolanaAPIClient {
return result.value
}

public func getTokenAccountsByDelegate(
public func getTokenAccountsByDelegate<T: TokenAccountLayoutState>(
pubkey: String,
mint: String? = nil,
programId: String? = nil,
configs: RequestConfiguration? = nil
) async throws -> [TokenAccount<SPLTokenAccountState>] {
let result: Rpc<[TokenAccount<SPLTokenAccountState>]> = try await get(
) async throws -> [TokenAccount<T>] {
let result: Rpc<[TokenAccount<T>]> = try await get(
method: "getTokenAccountsByDelegate",
params: [
pubkey,
Expand All @@ -161,12 +161,13 @@ public class JSONRPCAPIClient: SolanaAPIClient {
return result.value
}

public func getTokenAccountsByOwner(
public func getTokenAccountsByOwner<T: TokenAccountLayoutState>(
pubkey: String,
params: OwnerInfoParams? = nil,
configs: RequestConfiguration? = nil
) async throws -> [TokenAccount<SPLTokenAccountState>] {
let result: Rpc<[TokenAccount<SPLTokenAccountState>]> = try await get(
params: OwnerInfoParams?,
configs: RequestConfiguration?,
decodingTo _: T.Type
) async throws -> [TokenAccount<T>] {
let result: Rpc<[TokenAccount<T>]> = try await get(
method: "getTokenAccountsByOwner",
params: [pubkey, params, configs]
)
Expand Down
12 changes: 8 additions & 4 deletions Sources/SolanaSwift/APIClient/SolanaAPIClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ public protocol SolanaAPIClient {
/// - Returns The result will be an array of TokenAccount<AccountInfo>
/// - SeeAlso https://docs.solana.com/developing/clients/jsonrpc-api#gettokenaccountsbydelegate
///
func getTokenAccountsByDelegate(
func getTokenAccountsByDelegate<T: TokenAccountLayoutState>(
pubkey: String,
mint: String?,
programId: String?,
configs: RequestConfiguration?
) async throws -> [TokenAccount<SPLTokenAccountState>]
) async throws -> [TokenAccount<T>]

/// Returns all SPL Token accounts by token owner
/// - Parameters:
Expand All @@ -183,8 +183,12 @@ public protocol SolanaAPIClient {
/// - Returns The result will be an array of TokenAccount<AccountInfo>
/// - SeeAlso https://docs.solana.com/developing/clients/jsonrpc-api#gettokenaccountsbyowner
///
func getTokenAccountsByOwner(pubkey: String, params: OwnerInfoParams?, configs: RequestConfiguration?) async throws
-> [TokenAccount<SPLTokenAccountState>]
func getTokenAccountsByOwner<T: TokenAccountLayoutState>(
pubkey: String,
params: OwnerInfoParams?,
configs: RequestConfiguration?,
decodingTo: T.Type
) async throws -> [TokenAccount<T>]

/// Returns the 20 largest accounts of a particular SPL Token type
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,9 @@ public extension SolanaBlockchainClient {
from owner: PublicKey,
amount: Lamports,
payer: PublicKey,
minRentExemption mre: Lamports?
minRentExemption: Lamports
) async throws -> AccountInstructions {
let newAccount: KeyPair
let minRentExemption: Lamports
async let requestNewAccount = KeyPair(network: apiClient.endpoint.network)

if let mre = mre {
minRentExemption = mre
newAccount = try await requestNewAccount
} else {
(minRentExemption, newAccount) = try await (
apiClient.getMinimumBalanceForRentExemption(
dataLength: UInt64(SPLTokenAccountState.BUFFER_LENGTH),
commitment: "recent"
),
requestNewAccount
)
}
let newAccount = try await KeyPair(network: apiClient.endpoint.network)

return .init(
account: newAccount.publicKey,
Expand All @@ -39,7 +24,7 @@ public extension SolanaBlockchainClient {
from: owner,
toNewPubkey: newAccount.publicKey,
lamports: amount + minRentExemption,
space: SPLTokenAccountState.BUFFER_LENGTH,
space: TokenAccountState.BUFFER_LENGTH,
programId: TokenProgram.id
),
TokenProgram.initializeAccountInstruction(
Expand Down Expand Up @@ -72,19 +57,21 @@ public extension SolanaBlockchainClient {
func prepareForCreatingAssociatedTokenAccount(
owner: PublicKey,
mint: PublicKey,
tokenProgramId: PublicKey,
feePayer: PublicKey,
closeAfterward: Bool
) async throws -> AccountInstructions {
let associatedAddress = try PublicKey.associatedTokenAddress(
walletAddress: owner,
tokenMintAddress: mint
tokenMintAddress: mint,
tokenProgramId: tokenProgramId
)

let isAssociatedTokenAddressRegistered: Bool
do {
let info: BufferInfo<SPLTokenAccountState>? = try await apiClient
let info: BufferInfo<TokenAccountState>? = try await apiClient
.getAccountInfo(account: associatedAddress.base58EncodedString)
if info?.owner == TokenProgram.id.base58EncodedString,
if PublicKey.isSPLTokenProgram(info?.owner),
info?.data.owner == owner
{
isAssociatedTokenAddressRegistered = true
Expand Down Expand Up @@ -127,7 +114,8 @@ public extension SolanaBlockchainClient {
.createAssociatedTokenAccountInstruction(
mint: mint,
owner: owner,
payer: feePayer
payer: feePayer,
tokenProgramId: tokenProgramId
),
],
cleanupInstructions: cleanupInstructions,
Expand Down
Loading

0 comments on commit 48f98d4

Please sign in to comment.