Skip to content

Commit f87d83c

Browse files
committed
added NFT fetching for algorand
1 parent 24639a8 commit f87d83c

File tree

3 files changed

+230
-49
lines changed

3 files changed

+230
-49
lines changed

src/main/kotlin/id/walt/nftkit/rest/AlgorandNftController.kt

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import kotlinx.serialization.json.Json
1313

1414

1515
object AlgorandNftController{
16+
17+
private const val TAG = "Algorand Blockchain: Non-fungible tokens(NFTs)"
1618
fun accountCreation(ctx : Context){
1719
val result = AlgorandNftService.createAccount()
1820
ctx.json(result)
@@ -38,45 +40,86 @@ object AlgorandNftController{
3840

3941

4042

41-
fun fetchAssetMetadata(ctx: Context){
43+
44+
45+
fun fetchToken(ctx: Context){
4246
val chain =ctx.pathParam("chain")
43-
val asset = ctx.pathParam("asset")
44-
val nft = AlgorandNftService.getAssetMeatadata(
47+
val asset = ctx.pathParam("assetId")
48+
val response = AlgorandNftService.getToken(
4549
asset.toLong(),
4650
Common.getAlgorandChain(chain.uppercase())
4751
)
52+
ctx.json(response)
53+
}
54+
fun fetchTokenDocs()= document().operation {
55+
it.summary("Fetching Token")
56+
.operationId("fetchToken")
57+
.addTagsItem(TAG)}
58+
.pathParam<String>("chain") {
59+
it.schema<AlgorandChain>{}}
60+
.pathParam<String>("assetId"){}
61+
.json<AlgorandToken>("200"){
62+
it.description("Fetched token")
63+
}
64+
65+
///////////////////////////////////////////////////////////////////////////
4866

49-
ctx.result(Json.encodeToString(nft))
67+
fun fetchAssetMetadata(ctx: Context){
68+
val chain =ctx.pathParam("chain")
69+
val asset = ctx.pathParam("assetId")
70+
val response = AlgorandNftService.getAssetMeatadata(
71+
asset.toLong(),
72+
Common.getAlgorandChain(chain.uppercase())
73+
)
74+
ctx.json(response)
5075
}
5176
fun fetchAssetMetadataDocs()= document().operation {
52-
it.summary("Fetching Asset on Algorand Network")
77+
it.summary("Fetching token parametrs ")
5378
.operationId("fetchAlgornadAssets")
54-
.addTagsItem("Algorand Blockchain: Non-fungible tokens(NFTs)")}
79+
.addTagsItem(TAG)}
5580
.pathParam<String>("chain") {
5681
it.schema<AlgorandChain>{}}
57-
.pathParam<String>("asset"){}
58-
.json<TokenOwnersDataResponse>("200"){
59-
it.description("Fetched asset")
82+
.pathParam<String>("assetId"){}
83+
.json<Asset>("200"){
84+
it.description("Fetched token parameteres")
6085
}
6186

87+
///////////////////////////////////////////////////////////////////////////
6288

89+
fun fetchAccountAssets(ctx: Context){
90+
val chain = ctx.pathParam("chain")
91+
val address = ctx.pathParam("address")
92+
val response = AlgorandNftService.getAccountAssets(address, Common.getAlgorandChain(chain.uppercase()))
93+
ctx.json(response)
94+
}
95+
fun fetchAccountAssetsDocs()= document().operation {
96+
it.summary("Fetching account tokens ")
97+
.operationId("fetchAccountAssets")
98+
.addTagsItem(TAG)}
99+
.pathParam<String>("chain") {
100+
it.schema<AlgorandChain>{}}
101+
.pathParam<String>("address"){}
102+
.json<AssetHoldingsResponse>("200"){
103+
it.description("Fetched Tokens")
104+
}
105+
106+
///////////////////////////////////////////////////////////////////////////
63107

64108
fun fetchNftMetadata(ctx: Context){
65109
val chain =ctx.pathParam("chain")
66-
val asset = ctx.pathParam("asset")
110+
val asset = ctx.pathParam("assetId")
67111
val result = AlgorandNftService.getNftMetadata(asset.toLong(), Common.getAlgorandChain(chain.uppercase()) )
68112
ctx.result(Json.encodeToString(result))
69-
}
70113

114+
}
71115
fun fetchNftMetadataDocs()= document().operation {
72-
it.summary("Fetching NFTs on Algorand Network")
116+
it.summary("Fetching NFT metadata ")
73117
.operationId("fetchAlgornadNfts")
74-
.addTagsItem("Algorand Blockchain: Non-fungible tokens(NFTs)")}
118+
.addTagsItem(TAG)}
75119
.pathParam<String>("chain") {
76120
it.schema<AlgorandChain>{}}
77-
.pathParam<String>("asset"){}
78-
.json<String>("200"){
79-
it.description("Fetched NFT")
121+
.pathParam<String>("assetId"){}
122+
.json<AlgoNftMetadata>("200"){
123+
it.description("Fetched NFT metadata")
80124
}
81-
82125
}

src/main/kotlin/id/walt/nftkit/rest/NftKitApi.kt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,19 +281,29 @@ object NftKitApi {
281281
}
282282

283283
path("Algorand"){
284+
285+
post("account/create/",
286+
documented(AlgorandNftController.accountCreationDocs(), AlgorandNftController::accountCreation)
287+
)
288+
post("asset/create/{assetName}/{assetUnitName}/{url}/",
289+
documented(AlgorandNftController.assetCreationDocs(), AlgorandNftController::assetCreation)
290+
)
291+
284292
get(
285-
"chain/{chain}/asset/{asset}",
293+
"chain/{chain}/asset/{assetId}",
294+
documented(AlgorandNftController.fetchTokenDocs(), AlgorandNftController::fetchToken)
295+
)
296+
get(
297+
"chain/{chain}/asset/{assetId}/params",
286298
documented(AlgorandNftController.fetchAssetMetadataDocs(), AlgorandNftController::fetchAssetMetadata)
287299
)
288300
get(
289-
"chain/{chain}/asset/{asset}/nft",
301+
"chain/{chain}/asset/{assetId}/metadata",
290302
documented(AlgorandNftController.fetchNftMetadataDocs(), AlgorandNftController::fetchNftMetadata)
291303
)
292-
post("account/create/",
293-
documented(AlgorandNftController.accountCreationDocs(), AlgorandNftController::accountCreation)
294-
)
295-
post("asset/create/{assetName}/{assetUnitName}/{url}/",
296-
documented(AlgorandNftController.assetCreationDocs(), AlgorandNftController::assetCreation)
304+
get(
305+
"chain/{chain}/assets/account/{address}/",
306+
documented(AlgorandNftController.fetchAccountAssetsDocs(), AlgorandNftController::fetchAccountAssets)
297307
)
298308

299309
}

src/main/kotlin/id/walt/nftkit/services/AlgorandNftService.kt

Lines changed: 153 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@ import com.algorand.algosdk.transaction.Transaction
66
import com.algorand.algosdk.util.Encoder
77
import com.algorand.algosdk.v2.client.common.AlgodClient
88
import com.algorand.algosdk.v2.client.common.IndexerClient
9-
import com.algorand.algosdk.v2.client.model.Asset
9+
1010
import id.walt.nftkit.Values.ALGORAND_TESTNET_EXPLORER
1111
import id.walt.nftkit.metadata.IPFSMetadata
1212
import id.walt.nftkit.services.WaltIdServices.loadAlgorand
13+
import io.ktor.client.*
1314
import io.ktor.client.call.*
15+
import io.ktor.client.engine.cio.*
16+
import io.ktor.client.plugins.contentnegotiation.*
17+
import io.ktor.client.plugins.logging.*
1418
import io.ktor.client.request.*
19+
import io.ktor.http.*
20+
import io.ktor.serialization.kotlinx.json.*
1521
import kotlinx.coroutines.runBlocking
22+
import kotlinx.serialization.SerialName
1623
import kotlinx.serialization.Serializable
24+
import kotlinx.serialization.json.Json
1725
import kotlinx.serialization.json.JsonElement
1826

1927

@@ -29,18 +37,86 @@ data class AlgorandAccount(val address: String, val mnemonic: String)
2937
@Serializable
3038
data class AlgodResponse(val txId: String , val explorerUrl: String)
3139

40+
@Serializable
41+
data class AlgorandToken
42+
(
43+
var TokenParams: Asset?= null,
44+
var Metadata : AlgoNftMetadata? = null
45+
)
46+
47+
@Serializable
48+
data class Asset (
49+
var index: Long? = null,
50+
@SerialName("created-at-round")
51+
var createdAtRound: Long? = null,
52+
var deleted: Boolean? = null,
53+
@SerialName("destroyed-at-round")
54+
var destroyedAtRound: Long? = null,
55+
@SerialName("params")
56+
var params: AssetParams? = null
57+
){
58+
@Serializable
59+
data class AssetParams(
60+
var clawback: String? = null,
61+
var creator: String? = null,
62+
var decimals: Long? = null,
63+
@SerialName("default-frozen")
64+
var defaultFrozen: Boolean? = null,
65+
var freeze: String? = null,
66+
var manager: String? = null,
67+
var name: String? = null,
68+
var reserve: String? = null,
69+
var total: Long? = null,
70+
@SerialName("unit-name")
71+
var unitName: String? = null,
72+
var url: String? = null
73+
)
74+
}
75+
76+
77+
@Serializable
78+
data class AssetHoldingsResponse (
79+
var assets: List<AssetHolding> = ArrayList())
80+
{
81+
@Serializable
82+
data class AssetHolding(
83+
var amount: Int? = null,
84+
@SerialName("asset-id")
85+
var assetId: Long? = null,
86+
@SerialName("deleted")
87+
var deleted: Boolean? = null,
88+
@SerialName("is-frozen")
89+
var isFrozen: Boolean? = null,
90+
)
91+
}
92+
93+
3294
@Serializable
3395
data class AlgoNftMetadata (
3496
var name: String? = null,
3597
var description: String? = null,
3698
var image: String? = null,
3799
var decimals: Int? = null,
38100
var unitName: String? = null,
39-
var properties : Map<String, JsonElement> ? = null,
101+
//var properties : Map<String, JsonElement> ? = null,
40102
)
41103

42104
object AlgorandNftService {
43105

106+
val client1 = HttpClient(CIO.create { requestTimeout = 0 }) {
107+
install(ContentNegotiation) {
108+
json(Json {
109+
ignoreUnknownKeys = true
110+
})
111+
}
112+
install(Logging) {
113+
// logger = Logger.SIMPLE
114+
// level = LogLevel.ALL
115+
level = LogLevel.BODY
116+
}
117+
expectSuccess = false
118+
}
119+
44120
val ALGOD_API_ADDR = "https://testnet-algorand.api.purestake.io/ps2"
45121
val IDX_API_ADDR = "https://testnet-algorand.api.purestake.io/idx2"
46122

@@ -94,40 +170,92 @@ object AlgorandNftService {
94170
return AlgodResponse("Null" , "Null")
95171
}
96172

97-
fun getAssetMeatadata(assetId: Long, chain: AlgorandChain) : Asset {
173+
// fun getAssetMeatadata(assetId: Long, chain: AlgorandChain) : Asset {
174+
//
175+
// val ALGOD_API_ADDR = when (chain) {
176+
// AlgorandChain.MAINNET -> "https://mainnet-api.algonode.cloud"
177+
// AlgorandChain.TESTNET -> "https://testnet-api.algonode.cloud"
178+
// AlgorandChain.BETANET -> "https://betanet-api.algonode.cloud"
179+
// }
180+
// var client = AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN)
181+
//
182+
// return client.GetAssetByID(assetId).execute().body()
183+
//
184+
// }
185+
// fun getNftMetadata(assetId: Long, chain: AlgorandChain):AlgoNftMetadata{
186+
//
187+
// return runBlocking {
188+
//
189+
// val asset: Asset = getAssetMeatadata(assetId, chain)
190+
// var cid = (asset.params.url).substringAfter("ipfs://")
191+
// val nft = IPFSMetadata.client.get("https://ipfs.algonode.xyz/ipfs/$cid") {}.body<AlgoNftMetadata>()
192+
//
193+
// return@runBlocking nft;
194+
// }
195+
// }
98196

99-
val ALGOD_API_ADDR = when (chain) {
100-
AlgorandChain.MAINNET -> "https://mainnet-api.algonode.cloud"
101-
AlgorandChain.TESTNET -> "https://testnet-api.algonode.cloud"
102-
AlgorandChain.BETANET -> "https://betanet-api.algonode.cloud"
103-
}
104-
var client = AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN)
197+
fun getToken(assetId: Long, chain: AlgorandChain): AlgorandToken {
198+
return runBlocking {
199+
val ALGOD_API_ADDR = when (chain) {
200+
AlgorandChain.MAINNET -> "https://mainnet-api.algonode.cloud"
201+
AlgorandChain.TESTNET -> "https://testnet-api.algonode.cloud"
202+
AlgorandChain.BETANET -> "https://betanet-api.algonode.cloud"
203+
}
204+
val tokenParams = client1.get(ALGOD_API_ADDR + "/v2/assets/" + assetId){
205+
contentType(ContentType.Application.Json)
206+
}.body<Asset>()
105207

106-
return client.GetAssetByID(assetId).execute().body()
208+
var cid = (tokenParams.params?.url)?.substringAfter("ipfs://")
209+
val nft =
210+
IPFSMetadata.client.get("https://ipfs.io/ipfs/$cid")
211+
{ contentType(ContentType.Application.Json)}.body<AlgoNftMetadata>()
107212

108-
}
109-
fun getNftMetadata(assetId: Long, chain: AlgorandChain):AlgoNftMetadata{
213+
var result = AlgorandToken()
110214

215+
result.TokenParams = tokenParams
216+
result.Metadata = nft
217+
return@runBlocking result
218+
}
219+
}
220+
fun getAssetMeatadata(assetId: Long, chain: AlgorandChain): Asset {
111221
return runBlocking {
222+
val ALGOD_API_ADDR = when (chain) {
223+
AlgorandChain.MAINNET -> "https://mainnet-api.algonode.cloud"
224+
AlgorandChain.TESTNET -> "https://testnet-api.algonode.cloud"
225+
AlgorandChain.BETANET -> "https://betanet-api.algonode.cloud"
226+
}
227+
val asset = client1.get(ALGOD_API_ADDR + "/v2/assets/" + assetId){
228+
contentType(ContentType.Application.Json)
229+
}.body<Asset>()
112230

113-
val asset: Asset = getAssetMeatadata(assetId, chain)
114-
var cid = (asset.params.url).substringAfter("ipfs://")
115-
val nft = IPFSMetadata.client.get("https://ipfs.algonode.xyz/ipfs/$cid") {}.body<AlgoNftMetadata>()
231+
return@runBlocking asset
232+
}
233+
}
116234

235+
fun getNftMetadata(assetId: Long, chain: AlgorandChain): AlgoNftMetadata {
236+
return runBlocking {
237+
val asset: Asset = getAssetMeatadata(assetId, chain)
238+
var cid = (asset.params?.url)?.substringAfter("ipfs://")
239+
val nft =
240+
IPFSMetadata.client.get("https://ipfs.algonode.xyz/ipfs/$cid")
241+
{ contentType(ContentType.Application.Json)}.body<AlgoNftMetadata>()
117242
return@runBlocking nft;
118243
}
119244
}
120245

121-
fun getAccountAssets(address:String, chain: AlgorandChain):Any{
122-
var ALGOD_API_ADDR = when(chain){
123-
AlgorandChain.MAINNET -> "https://mainnet-algorand.api.purestake.io/idx2"
124-
AlgorandChain.TESTNET -> "https://testnet-algorand.api.purestake.io/idx2"
125-
AlgorandChain.BETANET -> "https://betanet-algorand.api.purestake.io/idx2"
246+
fun getAccountAssets(address: String, chain: AlgorandChain): AssetHoldingsResponse {
247+
return runBlocking {
248+
var ALGOD_API_ADDR = when (chain) {
249+
AlgorandChain.MAINNET -> "https://mainnet-idx.algonode.cloud"
250+
AlgorandChain.TESTNET -> "https://testnet-idx.algonode.cloud"
251+
AlgorandChain.BETANET -> "https://betanet-idx.algonode.cloud"
252+
}
253+
val add = Address(address);
254+
val result =
255+
client1.get(ALGOD_API_ADDR + "/v2/accounts/" + add + "/assets") {
256+
contentType(ContentType.Application.Json)
257+
}.body<AssetHoldingsResponse>()
258+
return@runBlocking result;
126259
}
127-
var client = AlgodClient(ALGOD_API_ADDR, ALGOD_PORT, ALGOD_API_TOKEN, ALGOD_API_TOKEN_KEY)
128-
val add = Address(address);
129-
130-
return 0;
131260
}
132-
133261
}

0 commit comments

Comments
 (0)