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

Feat/soulbound nft #139

Merged
merged 33 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8ae5d83
feat:added shimmer chain id
SuperBatata Aug 23, 2023
5370a1f
feat:created Iota-shimmer web3 provider
SuperBatata Aug 23, 2023
477a2c3
feat:created interface for soulbound smart contract functions
SuperBatata Aug 23, 2023
e204620
feat:mint and deploy soulbound token controllers
SuperBatata Aug 23, 2023
30f5310
feat:endpoint for soulbound token
SuperBatata Aug 23, 2023
60a368a
feat:added soulbound token standard
SuperBatata Aug 23, 2023
9c337fc
feat:added support to shimmer EvmChain
SuperBatata Aug 23, 2023
470beab
feat:created deploy soulbound contract
SuperBatata Aug 23, 2023
ba1e0a6
feat: mint function for soulbound token
SuperBatata Aug 23, 2023
9e70866
feat: add shimmer evm to providers
SuperBatata Aug 23, 2023
5807bcb
feat: soulbound.sol smart contract
SuperBatata Aug 23, 2023
d3afa63
feat: soulbound token standard
SuperBatata Aug 23, 2023
3b66009
feat: added shimmer chain id
SuperBatata Aug 23, 2023
b92def6
feat: added shimmer rpc support
SuperBatata Aug 23, 2023
1746a52
feat: added shimmer explorer url support
SuperBatata Aug 23, 2023
3f71818
fix: load soulbound contract function
SuperBatata Aug 25, 2023
c10b18e
fix: ownerof function
SuperBatata Aug 25, 2023
08c1be1
fix: name of smart contract function
SuperBatata Aug 25, 2023
fd31476
fix: symbol of smart contract function
SuperBatata Aug 25, 2023
39322fc
fix: tokenuri of smart contract function
SuperBatata Aug 25, 2023
8a108d1
fix: balanceOf function
SuperBatata Aug 25, 2023
e966906
fix: safemint function
SuperBatata Aug 25, 2023
b738e0b
fix: deploy contract function
SuperBatata Aug 25, 2023
21298d8
add owner of soulbound token service
SuperBatata Aug 25, 2023
1f85687
remove functions
SuperBatata Aug 25, 2023
b37bfa1
remove endpoints
SuperBatata Aug 25, 2023
c097166
remove functions from controller
SuperBatata Aug 25, 2023
ee7af7a
refactor : ISoulBoundTokenStandard
SuperBatata Aug 25, 2023
534b34b
feat:soulbound NFT smart contract
SuperBatata Aug 25, 2023
599a10c
feat:added support Interface for Soulbound smart contract
SuperBatata Aug 27, 2023
1b1ff37
feat:added soulbound smart contract to deployment function
SuperBatata Aug 27, 2023
a031b64
refactor:supportsInterface
SuperBatata Aug 27, 2023
5f4a25d
feat:supportsInterface function
SuperBatata Aug 27, 2023
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
459 changes: 459 additions & 0 deletions src/main/java/smart_contract_wrapper/SoulBoundTest.java

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/main/kotlin/id/walt/nftkit/Values.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ object Values {
const val POLYGON_TESTNET_MUMBAI_CHAIN_ID: Long = 80001
const val MOONBEAM_MAINNET_CHAIN_ID: Long = 1284
const val ASTAR_MAINNET_CHAIN_ID: Long = 592
const val SHIMMEREVM_TESTNET_CHAIN_ID: Long = 1072

const val ETHEREUM_MAINNET_SCAN_API_URL= "api.etherscan.io"
const val ETHEREUM_TESTNET_GOERLI_SCAN_API_URL = "api-goerli.etherscan.io"
Expand All @@ -23,6 +24,7 @@ object Values {
const val ETHEREUM_TESTNET_SEPOLIA_BLOCK_EXPLORER_URL = "https://sepolia.etherscan.io/"
const val POLYGON_MAINNET_BLOCK_EXPLORER_URL = "https://polygonscan.com"
const val POLYGON_TESTNET_MUMBAI_BLOCK_EXPLORER_URL = "https://mumbai.polygonscan.com"
const val SHIMMEREVM_TESTNET_BLOCK_EXPLORER_URL = "https://explorer.evm.testnet.shimmer.network"

const val ETHEREUM_MAINNET_ALCHEMY_URL = "https://eth-mainnet.alchemyapi.io/v2/"
const val ETHEREUM_TESTNET_GOERLI_ALCHEMY_URL = "https://eth-goerli.g.alchemy.com/v2/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ object Erc721TokenStandard : IErc721TokenStandard {
EVMChain.MUMBAI -> Values.POLYGON_TESTNET_MUMBAI_CHAIN_ID
EVMChain.ASTAR -> Values.ASTAR_MAINNET_CHAIN_ID
EVMChain.MOONBEAM -> Values.MOONBEAM_MAINNET_CHAIN_ID
EVMChain.SHIMMEREVM -> Values.SHIMMEREVM_TESTNET_CHAIN_ID
}
val transactionManager: TransactionManager = RawTransactionManager(
web3j, credentials, chainId
Expand All @@ -317,4 +318,4 @@ object Erc721TokenStandard : IErc721TokenStandard {
}


}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package id.walt.nftkit.chains.evm.erc721
import id.walt.nftkit.services.Chain
import id.walt.nftkit.services.EVMChain
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.Bool
import org.web3j.abi.datatypes.DynamicBytes
import org.web3j.abi.datatypes.Utf8String
import org.web3j.abi.datatypes.generated.Uint256
import org.web3j.protocol.core.RemoteFunctionCall
import org.web3j.protocol.core.methods.response.TransactionReceipt
import java.math.BigInteger
interface ISoulBoundTokenStandard {

fun safeMint(chain: EVMChain, contractAddress: String,to: String, uri: String): TransactionReceipt?
fun ownerOf(chain: EVMChain, contractAddress: String, tokenId: Uint256):String?

fun name(chain: EVMChain, contractAddress: String): String?

fun symbol(chain: EVMChain, contractAddress: String): String?

fun tokenURI(chain: EVMChain, contractAddress: String, tokenId: Uint256): String?

fun balanceOf(chain: EVMChain, contractAddress: String, owner: Address): BigInteger?

fun safeTransferFrom(chain: EVMChain, contractAddress: String, from: Address, to: Address, tokenId: Uint256, signedAccount: String?): TransactionReceipt

fun supportsInterface(chain: EVMChain, contractAddress: String): Boolean

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package id.walt.nftkit.chains.evm.erc721

import id.walt.nftkit.Values
import id.walt.nftkit.WaltIdGasProvider
import id.walt.nftkit.services.*
import id.walt.nftkit.utilis.providers.ProviderFactory
import org.web3j.abi.datatypes.Address
import org.web3j.abi.datatypes.generated.Bytes4
import org.web3j.abi.datatypes.generated.Uint256
import org.web3j.crypto.Credentials
import org.web3j.protocol.core.RemoteCall
import org.web3j.protocol.core.methods.response.TransactionReceipt
import org.web3j.tx.RawTransactionManager
import org.web3j.tx.TransactionManager
import org.web3j.tx.gas.ContractGasProvider
import org.web3j.utils.Numeric
import smart_contract_wrapper.SoulBoundTest
import java.math.BigInteger

object SoulBoundTokenStandard : ISoulBoundTokenStandard {






private fun loadContract(chain: EVMChain, address: String, signedAccount: String? ="") : SoulBoundTest {
val web3j = ProviderFactory.getProvider(chain)?.getWeb3j()

val privateKey: String = if(signedAccount == null || "" == (signedAccount)){
WaltIdServices.loadChainConfig().privateKey
}else{
val lowercaseAddress= WaltIdServices.loadAccountKeysConfig().keys.mapKeys { it.key.lowercase() }
lowercaseAddress[signedAccount.lowercase()]!!
}

val credentials: Credentials = Credentials.create(privateKey)

val gasProvider: ContractGasProvider = WaltIdGasProvider
val chainId= when(chain){
EVMChain.ETHEREUM -> Values.ETHEREUM_MAINNET_CHAIN_ID
EVMChain.GOERLI -> Values.ETHEREUM_TESTNET_GOERLI_CHAIN_ID
EVMChain.SEPOLIA -> Values.ETHEREUM_TESTNET_SEPOLIA_CHAIN_ID
EVMChain.POLYGON -> Values.POLYGON_MAINNET_CHAIN_ID
EVMChain.MUMBAI -> Values.POLYGON_TESTNET_MUMBAI_CHAIN_ID
EVMChain.ASTAR -> Values.ASTAR_MAINNET_CHAIN_ID
EVMChain.MOONBEAM -> Values.MOONBEAM_MAINNET_CHAIN_ID
EVMChain.SHIMMEREVM -> Values.SHIMMEREVM_TESTNET_CHAIN_ID
}
val transactionManager: TransactionManager = RawTransactionManager(
web3j, credentials, chainId
)
return SoulBoundTest.load(address, web3j,transactionManager,gasProvider)

}


override fun ownerOf(chain: EVMChain, contractAddress: String, tokenId: Uint256): String? {
return loadContract(chain, contractAddress).ownerOf(tokenId).send().value
}

override fun name(chain: EVMChain, contractAddress: String): String? {
val contract = loadContract(chain, contractAddress)
return contract.name().send().value
}

override fun symbol(chain: EVMChain, contractAddress: String): String? {
return loadContract(chain, contractAddress).symbol().send().value
}

override fun tokenURI(chain: EVMChain, contractAddress: String, tokenId: Uint256): String? {
return loadContract(chain, contractAddress).tokenURI(tokenId).send().value
}

override fun balanceOf(chain: EVMChain, contractAddress: String, owner: Address): BigInteger? {
return loadContract(chain, contractAddress).balanceOf(owner).send().value
}

override fun safeTransferFrom(
chain: EVMChain,
contractAddress: String,
from: Address,
to: Address,
tokenId: Uint256,
signedAccount: String?
): TransactionReceipt {
TODO("Not yet implemented")
}

override fun safeMint(chain: EVMChain, contractAddress: String, to: String, uri: String): TransactionReceipt? {
return loadContract(chain, contractAddress).safeMint(to, uri).send()
}


fun deployContract(chain: EVMChain) : DeploymentResponse {
val chainId= when(chain){
EVMChain.ETHEREUM -> Values.ETHEREUM_MAINNET_CHAIN_ID
EVMChain.GOERLI -> Values.ETHEREUM_TESTNET_GOERLI_CHAIN_ID
EVMChain.SEPOLIA -> Values.ETHEREUM_TESTNET_SEPOLIA_CHAIN_ID
EVMChain.POLYGON -> Values.POLYGON_MAINNET_CHAIN_ID
EVMChain.MUMBAI -> Values.POLYGON_TESTNET_MUMBAI_CHAIN_ID
EVMChain.ASTAR -> Values.ASTAR_MAINNET_CHAIN_ID
EVMChain.MOONBEAM -> Values.MOONBEAM_MAINNET_CHAIN_ID
EVMChain.SHIMMEREVM -> Values.SHIMMEREVM_TESTNET_CHAIN_ID
}

val web3j = ProviderFactory.getProvider(chain)?.getWeb3j()
val credentials: Credentials = Credentials.create(WaltIdServices.loadChainConfig().privateKey)
val gasProvider: ContractGasProvider = WaltIdGasProvider
val remotCall: RemoteCall<SoulBoundTest>
val transactionManager: TransactionManager = RawTransactionManager(
web3j, credentials, chainId
)
remotCall = SoulBoundTest.deploy(web3j, transactionManager, gasProvider)

val contract = remotCall.send()

val url = WaltIdServices.getBlockExplorerUrl(chain)
val ts = TransactionResponse(
contract.transactionReceipt.get().transactionHash,
"$url/tx/${contract.transactionReceipt.get().transactionHash}"
)
return DeploymentResponse(ts, contract.contractAddress, "$url/address/${contract.contractAddress}")
}


override fun supportsInterface(chain: EVMChain, contractAddress: String) : Boolean {
val erc721URIStorageWrapper = loadContract(chain, contractAddress)
val data = Numeric.hexStringToByteArray("0x5b5e139f") // ERC721 interface id
val interfaceId = Bytes4(data)
return erc721URIStorageWrapper.supportsInterface(interfaceId).send().value
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/id/walt/nftkit/rest/NftController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ object NftController {
}.json<DeploymentResponse>("200") { it.description("Transaction ID and smart contract address") }







fun mint(ctx: Context) {
val mintReq = ctx.bodyAsClass(MintRequest::class.java)
val chain = ctx.pathParam("chain")
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/id/walt/nftkit/rest/NftKitApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ object NftKitApi {
"chain/{chain}/contract/deploy",
documented(NftController.deployDocs(), NftController::deploy)
)

post(
"chain/{chain}/contract/{contractAddress}/token/mint",
documented(NftController.mintDocs(), NftController::mint)
Expand Down
36 changes: 32 additions & 4 deletions src/main/kotlin/id/walt/nftkit/services/NftService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package id.walt.nftkit.services

import id.walt.nftkit.Values
import id.walt.nftkit.chains.evm.erc721.Erc721TokenStandard
import id.walt.nftkit.chains.evm.erc721.SoulBoundTokenStandard
import id.walt.nftkit.metadata.IPFSMetadata
import id.walt.nftkit.metadata.MetadataUri
import id.walt.nftkit.metadata.MetadataUriFactory
Expand Down Expand Up @@ -95,7 +96,8 @@ enum class EVMChain {
SEPOLIA,
MUMBAI,
ASTAR,
MOONBEAM
MOONBEAM,
SHIMMEREVM,
}

enum class TokenStandard {
Expand Down Expand Up @@ -281,9 +283,14 @@ object NftService {
}

fun deploySmartContractToken(chain: EVMChain, parameter: DeploymentParameter, options: DeploymentOptions): DeploymentResponse {
return Erc721TokenStandard.deployContract(chain, parameter, options)
return if (parameter.options.transferable){
Erc721TokenStandard.deployContract(chain, parameter, options)
}else{
SoulBoundTokenStandard.deployContract(chain)
}
}


fun mintToken(
chain: EVMChain,
contractAddress: String,
Expand All @@ -302,6 +309,7 @@ object NftService {
return mintNewToken(parameter.recipientAddress, tokenUri, chain, contractAddress)
}


fun getNftMetadata(chain: EVMChain, contractAddress: String, tokenId: BigInteger): NftMetadata {
var uri = getMetadatUri(chain, contractAddress, tokenId)
if(Common.getMetadataType(uri).equals(MetadataStorageType.ON_CHAIN)){
Expand Down Expand Up @@ -336,6 +344,10 @@ object NftService {
return String()
}

fun ownerOfSoulbound( chain: EVMChain , contractAddress: String , tokenId: Uint256) : String{
return SoulBoundTokenStandard.ownerOf(chain, contractAddress, tokenId).toString()
}

fun transferFrom(chain: EVMChain, contractAddress: String, from: String, to: String, tokenId: BigInteger, signedAccount: String?): TransactionResponse {
val transactionReceipt = Erc721TokenStandard.transferFrom(chain, contractAddress, Address(from), Address(to), Uint256(tokenId), signedAccount)
return Common.getTransactionResponse(chain, transactionReceipt)
Expand Down Expand Up @@ -573,7 +585,7 @@ object NftService {
chain: EVMChain,
contractAddress: String
): MintingResponse {
if (isErc721Standard(chain, contractAddress) == true) {
if (isErc721Standard(chain, contractAddress) && !isSoulBoundStandard(chain , contractAddress)) {
//val erc721TokenStandard = Erc721TokenStandard()
val recipient = Address(recipientAddress)
val tokenUri = Utf8String(metadataUri)
Expand All @@ -587,12 +599,25 @@ object NftService {
TransactionResponse(transactionReceipt!!.transactionHash, "$url/tx/${transactionReceipt.transactionHash}")
val mr = MintingResponse(ts, eventValues?.indexedValues?.get(2)?.value as BigInteger)
return mr
} else {
} else if (isSoulBoundStandard(chain , contractAddress) && isErc721Standard(chain, contractAddress)){
val recipient = recipientAddress
val tokenUri = metadataUri
val transactionReceipt: TransactionReceipt? =
SoulBoundTokenStandard.safeMint(chain, contractAddress, recipient, tokenUri)
val eventValues: EventValues? =
staticExtractEventParameters(Erc721OnchainCredentialWrapper.TRANSFER_EVENT, transactionReceipt?.logs?.get(0))

val url = WaltIdServices.getBlockExplorerUrl(chain)
val ts =
TransactionResponse(transactionReceipt!!.transactionHash, "$url/tx/${transactionReceipt.transactionHash}")
val mr = MintingResponse(ts, eventValues?.indexedValues?.get(2)?.value as BigInteger)
return mr
}
return MintingResponse(null, null)
}



private fun getMetadatUri(chain: EVMChain, contractAddress: String, tokenId: BigInteger): String {
if (isErc721Standard(chain, contractAddress) == true) {
return Erc721TokenStandard.tokenURI(chain, contractAddress, Uint256(tokenId))
Expand All @@ -604,6 +629,9 @@ object NftService {
private fun isErc721Standard(chain: EVMChain, contractAddress: String): Boolean {
return Erc721TokenStandard.supportsInterface(chain, contractAddress)
}
private fun isSoulBoundStandard(chain: EVMChain, contractAddress: String): Boolean {
return SoulBoundTokenStandard.supportsInterface(chain, contractAddress)
}

private fun parseNftEvmMetadataResult(nft: JsonObject): NftMetadata{
var attributes: List<NftMetadata.Attributes>?=null
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/id/walt/nftkit/services/WaltIdServices.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.io.File
import java.util.*


data class Providers(val ethereum: String, val goerli: String, val sepolia: String, val polygon: String, val mumbai: String, val astar: String, val moonbeam: String, val opal: String, val unique: String)
data class Providers(val ethereum: String, val goerli: String, val sepolia: String, val polygon: String, val mumbai: String, val astar: String, val moonbeam: String, val opal: String, val unique: String , val shimmerevm: String)

data class ChainConfig(val providers: Providers, val privateKey: String)

Expand Down Expand Up @@ -104,6 +104,7 @@ object WaltIdServices {
EVMChain.SEPOLIA -> Values.ETHEREUM_TESTNET_SEPOLIA_BLOCK_EXPLORER_URL
EVMChain.POLYGON -> Values.POLYGON_MAINNET_BLOCK_EXPLORER_URL
EVMChain.MUMBAI -> Values.POLYGON_TESTNET_MUMBAI_BLOCK_EXPLORER_URL
EVMChain.SHIMMEREVM -> Values.SHIMMEREVM_TESTNET_BLOCK_EXPLORER_URL
else -> {throw Exception("${chain.toString()} is not supported")}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/kotlin/id/walt/nftkit/utilis/providers/IotaWeb3.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package id.walt.nftkit.utilis.providers

import id.walt.nftkit.services.WaltIdServices
import org.web3j.protocol.Web3j
import org.web3j.protocol.http.HttpService

class IotaWeb3 : Web3jInstance {
override fun getWeb3j(): Web3j {
return Web3j.build(HttpService(WaltIdServices.loadChainConfig().providers.shimmerevm))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package id.walt.nftkit.utilis.providers

import id.walt.nftkit.services.Chain
import id.walt.nftkit.services.EVMChain

object ProviderFactory {
Expand All @@ -13,6 +12,7 @@ object ProviderFactory {
EVMChain.MUMBAI -> MumbaiWeb3()
EVMChain.ASTAR -> AstarWeb3()
EVMChain.MOONBEAM -> MoonbeamWeb3()
EVMChain.SHIMMEREVM -> IotaWeb3()
}
}

5 changes: 3 additions & 2 deletions src/main/resources/walt-default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ providers:
moonbeam: "https://rpc.api.moonbeam.network"
unique: "https://rpc.unique.network"
opal: "https://rpc-opal.unique.network"
shimmerevm: https://json-rpc.evm.testnet.shimmer.network


privateKey: "bd4cb3e507f342ee3a710370cef39dda48f17b0a158b0b8dd3f000fbd5b2c2d9"

#privateKey: "bd4cb3e507f342ee3a710370cef39dda48f17b0a158b0b8dd3f000fbd5b2c2d9"
privateKey: "fe96ea369abac7818a890da598d40e3e4709f1150e3ff35910f77f63484cffba"
#privateKey: "a1fcab9b58015f452c9a89d4cde4807a80111ab27142730bfb96be936e576be1"

keys:
Expand Down
Loading
Loading