SDI for dapp developers
This page describes how to interact with the x/compliance module in order to get relevant information about the verified users and issuers.
If you need help on how to get your contract verified as an issuer and start verifying users, you can read check this section: https://swisstronik.gitbook.io/swisstronik-docs/development/swisstronik-cli/for-sdi-issuers
Examples of how to interact with Swisstronik smart contracts using different frontend libraries is available here: https://github.com/SigmaGmbH/swisstronik-dapp-template
Interacting with the x/compliance module using the SwisstronikJS SDK
You'll need to install @swisstronik/sdk npm package
import { SwisstronikStargateClient } from "@swisstronik/sdk";
const client = await SwisstronikStargateClient.connect(
"https://rpc.testnet.swisstronik.com"
);
Getting address details
It returns details for a verified address in the x/compliance module. You can use this method for both verified issuers and verified users.
const address = "swtr...";
const addressDetails = await client.queryAddressDetails(address);
The result contains information about whether the address is verified as an issuer or not, array of issuers verifications or if the verification has been revoked. Please, note that if the user address is verified by an issuer, the isVerified attribute will be false since it's true just for verified issuers. If the user is verified by an issuer, the verification array will contain elements such as the verification Id, verification type and issuer address.
Getting Issuer details
It returns issuer details by its address (name, description, url, logo and legal entity).
const issuerAddress = "swtr...";
const issuerDetails = await client.queryIssuerDetails(issuerAddress);
Getting verification details
It returns verification details by its Id (verification type, expiration timestamp, issuance timestamp, issuer address, etc.).
const verificationID = "AKsIPk2b...";
const verificationDetails = await client.queryVerificationDetails(verificationID);
Getting verified address list
It returns the list of verified addresses details for both users and issuers.
const addressList = await client.queryAddressList();
Getting Issuer list
It returns the list of Issuer details.
const issuerList = await client.queryIssuerList();
Getting verification details list
It returns the list of Verification details.
const verificationList = await client.queryVerificationList();
Working demo on https://sigmagmbh.github.io/swisstronik-issuer-registry-frontend/
Interacting with the x/compliance module using Solidity
In order to get relevant x/compliance information, there's a contract deployed and ready to be used called SWTRProxy
. Its code and latest address is deployed available on https://github.com/SigmaGmbH/swisstronik-sdi-contracts.
For interacting with the SWTRProxy
contract in your solidity contract, you can install the @swisstronik/sdi-contracts
npm package and use the ISWTRProxy
interface in your contract.
How to import and initialize the SWTRProxy
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {ISWTRProxy} from "@swisstronik/sdi-contracts/contracts/interfaces/ISWTRProxy.sol";
contract Sample {
ISWTRProxy public swtrProxy;
constructor(ISWTRProxy _swtrProxy) { swtrProxy = _swtrProxy; }
}
From there you can call any function available in the ISWTRProxy
interface
Getting list of verified Issuers
uint256 totalIssuers = swtrProxy.issuerRecordCount();
ISWTRProxy.Issuer[] memory issuers;
if(totalIssuers > 0) {
issuers = swtrProxy.listIssuersRecord(0, totalIssuers); //pagination
}
It'll return issuers name, address, and version.
Get issuer by address
ISWTRProxy.Issuer memory issuer = swtrProxy.getIssuerRecordByAddress(0x123..);
Get Issuer Addresses by name and versions
There may be multiple versions/addresses for the same issuer name
uint32[] memory versions; //add your versions
address[] memory issuerAddresses = swtrProxy.getIssuerAddressesByNameAndVersions("Quadrata", versions);
The addresses are sorted in the same order the versions are passed.
Checking if an user is verified with a specific verification type by any issuer
bool isVerified = swtrProxy.isUserVerified(
0x123.., //user address
ISWTRProxy.VerificationType.VT_KYC
);
Checking if an user is verified with a specific verification type by specific issuers
address[] memory allowedIssuers; //add your issuers address
bool isVerifiedByIssuers = swtrProxy.isUserVerifiedBy(
0x123.., //user address
ISWTRProxy.VerificationType.VT_KYC,
allowedIssuers
);
Getting verification details
bytes memory verificationId; // add your verification id
ISWTRProxy.VerificationData memory verificationDetails = swtrProxy.getVerificationDataById(
0x123, // user address
0x456, // issuer address
verificationId
);
Getting verification details list
ISWTRProxy.VerificationData[] memory verificationList = swtrProxy.listVerificationData(
0x123, // user address
0x456, // issuer address
);
Checking if user has a specific verification type
bool passed = swtrProxy.passedVerificationType(
0x123, // user address
0x456, // issuer address
ISWTRProxy.VerificationType.VT_HUMANITY // verification type
);
You can find the list of available functions here: https://github.com/SigmaGmbH/swisstronik-sdi-contracts
Solidity Examples
Getting an image url ONLY IF the user is has a specific verification type issued by a specific issuer
In this example only users with the VT_HUMANITY
verification type issued by the specified issuer are able to view the image set by the owner. Note due to the Intel SGX, there's no other way to view the image.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ISWTRProxy} from "@swisstronik/sdi-contracts/contracts/interfaces/ISWTRProxy.sol";
enum AdapterType {
Quadrata,
WorldCoin
}
contract Sample is Ownable, EIP712 {
string internal imageUrl; // Thanks to Intel SGX encryption, there's no way to know the value of this variable by checking the storage slot
ISWTRProxy public swtrProxy;
constructor(
ISWTRProxy _swtrProxy
) EIP712("Sample Dapp", "1") Ownable(msg.sender) {
swtrProxy = _swtrProxy;
}
function setImageUrl(string memory _imageUrl) public onlyOwner {
imageUrl = _imageUrl;
}
function getImageUrl(
bytes calldata signature
) public view returns (string memory) {
require(bytes(imageUrl).length > 0, "Image URL not set");
address userAddress = recoverSigner(signature);
uint32[] memory versions = new uint32[](1);
versions[0] = 1;
address[] memory allowedIssuers = swtrProxy.getIssuerAddressesByNameAndVersions(
"Worldcoin",
versions
);
bool isVerified = swtrProxy.isUserVerifiedBy(
userAddress,
ISWTRProxy.VerificationType.VT_HUMANITY,
allowedIssuers
);
require(isVerified, "User not verified");
return imageUrl;
}
function recoverSigner(
bytes calldata signature
) public view returns (address) {
bytes32 structHash = keccak256(
abi.encode(
keccak256("Obj(string contents)"),
keccak256("Sample Verification")
)
);
bytes32 digest = _hashTypedDataV4(structHash);
return ECDSA.recover(digest, signature);
}
}
Getting decoded original data of the verification by user address
In thes example, we get the decoded original data of the user verification
function decodeQuadrataPassportV1OriginalData(
address userAddress
)
public
view
returns (
uint8 aml,
string memory country,
string memory did,
bool isBusiness,
bool investorStatus
)
{
uint32[] memory versions = new uint32[](1);
versions[0] = 1;
address issuerAddress = swtrProxy.getIssuerAddressesByNameAndVersions(
"Quadrata",
versions
)[0];
ISWTRProxy.VerificationData[] memory verificationList = swtrProxy
.listVerificationData(userAddress, issuerAddress);
require(verificationList.length > 0, "No verification data found");
(aml, country, did, isBusiness, investorStatus) = swtrProxy
.decodeQuadrataPassportV1OriginalData(
verificationList[0].originalData
);
}
function decodeWorldcoinV1OriginalData(
address userAddress
)
public
view
returns (
string memory merkle_root,
string memory nullifier_hash,
string memory proof,
string memory verification_level
)
{
uint32[] memory versions = new uint32[](1);
versions[0] = 1;
address issuerAddress = swtrProxy.getIssuerAddressesByNameAndVersions(
"Worldcoin",
versions
)[0];
ISWTRProxy.VerificationData[] memory verificationList = swtrProxy
.listVerificationData(userAddress, issuerAddress);
require(verificationList.length > 0, "No verification data found");
(
merkle_root,
nullifier_hash,
proof,
verification_level
) = swtrProxy.decodeWorldcoinV1OriginalData(
verificationList[0].originalData
);
}
Typescript examples
Using web3.js and Swisstronik plugin order to check if an user has a specific verification type issued by a specific issuer
You'll need to install the plugin with npm i @swisstronik/web3-plugin-swisstronik
import { SwisstronikPlugin } from "@swisstronik/web3-plugin-swisstronik";
import { verificationTypes } from "@swisstronik/sdk/compliance/verificationDetails";
import { Web3 } from "web3";
const web3 = new Web3(walletProvider);
web3.registerPlugin(new SwisstronikPlugin("https://json-rpc.testnet.swisstronik.com"));
const contract = new web3.eth.Contract(ABI, CONTRACT_ADDRESS); // SWTRProxy ABI and Contract address
const verificationType = "VT_HUMANITY";
const issuerAddress = (
await contract
.methods
.getIssuerAddressesByNameAndVersions("Quadrata", [1])
.call()
) as string[][0];
const isVerified: boolean = contract.methods.isUserVerifiedBy(
"0x123", //user address,
verificationTypes.indexOf(verificationType), //enum
issuerAddress
).call();
console.log(isVerified);
Getting decoded original data of the verification by user address
const decodeQuadrataPassportV1OriginalData = async (userAddress: string) => {
const addressDetails = await client?.queryAddressDetails(userAddress);
const verificationsList = addressDetails?.verifications;
const issuerAddress = (await contract.methods
.getIssuerAddressesByNameAndVersions("Quadrata", [1])
.call()) as string[][0];
const verificationId = verificationsList?.find(
(v) => v.issuerAddress === issuerAddress
)?.verificationId;
const verificationDetails = await client?.queryVerificationDetails(
verificationId!
);
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
const originalData = "0x"+Buffer.from(verificationDetails?.originalData!,
"base64"
).toString("hex");
const { aml, country, did, isBusiness, investorStatus } =
web3.eth.abi.decodeParameters(
[
{
name: "aml",
type: "uint8",
},
{
name: "country",
type: "string",
},
{
name: "did",
type: "string",
},
{
name: "isBusiness",
type: "bool",
},
{
name: "investorStatus",
type: "bool",
},
],
originalData
);
};
const decodeWorldcoinV1OriginalData = async (userAddress: string) => {
const addressDetails = await client?.queryAddressDetails(userAddress);
const verificationsList = addressDetails?.verifications;
const issuerAddress = (await contract.methods
.getIssuerAddressesByNameAndVersions("Worldcoin", [1])
.call()) as string[][0];
const verificationId = verificationsList?.find(
(v) => v.issuerAddress === issuerAddress
)?.verificationId;
const verificationDetails = await client?.queryVerificationDetails(
verificationId!
);
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
const originalData = "0x"+Buffer.from(verificationDetails?.originalData!,
"base64"
).toString("hex");
const { merkle_root, nullifier_hash, proof, verification_level } =
web3.eth.abi.decodeParameters(
[
{
name: "merkle_root",
type: "string",
},
{
name: "nullifier_hash",
type: "string",
},
{
name: "proof",
type: "string",
},
{
name: "verification_level",
type: "string",
},
],
originalData
);
};
Note that via the SDK the addresses are specified in Bech32 (cosmos) format, and in solidity in ETH format. In some situations you may want/need to convert from Bech32 to ETH format. Or the other way around. You can do it with the @swisstronik/utils
package:
import { ethAddressToBech32, bech32toEthAddress } from "@swisstronik/utils";
Last updated