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

Token 2022 Support #85

Merged
merged 42 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cd2072c
feat: add token-2022 program
bigearsenal Jan 4, 2024
3295604
fix: folder structure
bigearsenal Jan 4, 2024
169dd60
feat: Token2022 BufferLayout
bigearsenal Jan 4, 2024
832c0ad
Merge branch 'main' into feature/token-2022
bigearsenal Jan 4, 2024
987a380
feat: Token2022ProgramTests
bigearsenal Jan 4, 2024
7208343
feat: separate tests
bigearsenal Jan 4, 2024
de8506d
feat: getTokenAccounts
bigearsenal Jan 5, 2024
f006281
feat: add tests
bigearsenal Jan 5, 2024
0ac32b0
fix: programId
bigearsenal Jan 5, 2024
e3ee72a
feat: mintdata
bigearsenal Jan 5, 2024
0e4bc8e
fix: optional type
bigearsenal Jan 5, 2024
2e52217
fix: tokenMetadata
bigearsenal Jan 5, 2024
042ed3b
feat: add minRentExempt
bigearsenal Jan 9, 2024
8babbb7
feat: some fixes
bigearsenal Jan 9, 2024
80c8a71
feat: define program to send
bigearsenal Jan 9, 2024
04d339d
feat: add lamportsPerSignature
bigearsenal Jan 11, 2024
a1d3cec
feat: remove generic
bigearsenal Jan 11, 2024
af02f17
feat: reverse
bigearsenal Jan 11, 2024
f1b8802
Merge branch 'feature/remove-generic' into feature/token-2022
bigearsenal Jan 11, 2024
0cc2f5d
fix: update CHANGELOG.md
bigearsenal Jan 11, 2024
454c363
feat: mock data
bigearsenal Jan 11, 2024
afab465
feat: add test for getAccountBalances
bigearsenal Jan 12, 2024
99d2cde
Merge branch 'feature/get-account-balances-tests' into feature/token-…
bigearsenal Jan 12, 2024
2cbdb61
fix: getAccountBalances
bigearsenal Jan 12, 2024
44fe66b
Merge branch 'main' into feature/token-2022
TrGiLong Jan 15, 2024
0edf5bc
feat: prepare for parsing extension
bigearsenal Jan 22, 2024
c2c81bd
feat: parse extensions
bigearsenal Jan 23, 2024
40bc44e
feat: add test cases
bigearsenal Jan 23, 2024
ad3580b
feat: some fixes
bigearsenal Jan 23, 2024
5f465c5
feat: InterestBearingConfig
bigearsenal Jan 23, 2024
acdbf73
feat: convenience method for getting extension
bigearsenal Jan 23, 2024
68509d8
feat: getParsedExtension
bigearsenal Jan 23, 2024
e072989
Merge branch 'feature/token-2022-extension' into feature/token-2022
bigearsenal Jan 23, 2024
e398f69
Update README.md
bigearsenal Jan 24, 2024
11f824f
Update README.md
bigearsenal Jan 24, 2024
5827e9f
fix: encoding tests
bigearsenal Jan 24, 2024
4100ad8
feat: VecU8
bigearsenal Jan 24, 2024
e66f37d
feat: VecU8
bigearsenal Jan 24, 2024
352b154
fix: encoding
bigearsenal Jan 24, 2024
40bf0b0
feat: MintLayoutState
bigearsenal Jan 24, 2024
c67f9a7
feat: rename TokenAccountState & TokenMintState
bigearsenal Jan 24, 2024
5c3b9b9
Merge pull request #89 from p2p-org/feature/token-2022-renaming
bigearsenal Jan 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix docs mre: The min rent exemption (leave it nil if there is no pre-defined)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mre can be non optional now since this info can be retrieved directly from getTokenAccountByOwners, no need to calculate it again

) 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