Contract.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import { Web3Networks } from "./Web3Networks";
  2. export class Web3Result {
  3. universalProvider: any;
  4. evmProvider: any;
  5. // chainType: Web3Networks;
  6. account: any;
  7. _signer: any;
  8. paymentProvider: any;
  9. public get signer(): any {
  10. if (this._signer == null) {
  11. if (this.evmProvider != null && this.evmProvider.isCon) {
  12. this._signer = this.evmProvider.getSigner();
  13. }
  14. }
  15. return this._signer;
  16. }
  17. public set signer(signer: any) {
  18. this._signer = signer;
  19. }
  20. public get success(): boolean {
  21. if (this.account == null || this.account == "") {
  22. return false;
  23. }
  24. if (this.evmProvider == null) {
  25. return false;
  26. }
  27. return true;
  28. }
  29. public static async formatEther(balance: any): Promise<number> {
  30. // @ts-ignore
  31. return Number(Number(window.ethers.utils.formatEther(balance)).toFixed(8));
  32. }
  33. public async getBalance(): Promise<number> {
  34. const wei = await this.evmProvider.getBalance(this.account);
  35. return await Web3Result.formatEther(wei);
  36. }
  37. }
  38. export enum ContractResult {
  39. WalletConnectError = "wallet connect error",
  40. SUCCESS = "all success",
  41. NOT_OWNER = "not owner",
  42. NOT_APPROVED = "not approved",
  43. GAS_ERROR = "gas error",
  44. EXECUTION_ERROR = "execution error",
  45. }
  46. export interface ContractError extends Error {
  47. code?: string;
  48. data?: any;
  49. }
  50. export class Contract {
  51. private static readonly ERROR_NOT_OWNER = "not owner";
  52. private static readonly ERROR_NOT_APPROVED = "not approved";
  53. private static readonly ERROR_GAS = "gas error";
  54. private static readonly ERROR_EXECUTION = "execution error";
  55. private readonly web3Result: Web3Result;
  56. private readonly abi: any;
  57. private readonly contractAddress: string;
  58. private contract: any;
  59. private constructor(
  60. web3Result: Web3Result,
  61. abi: any,
  62. contractAddress: string
  63. ) {
  64. this.web3Result = web3Result;
  65. this.abi = abi;
  66. this.contractAddress = contractAddress;
  67. }
  68. public static async build(
  69. web3Result: Web3Result,
  70. abi: any,
  71. contractAddress: string
  72. ): Promise<Contract> {
  73. const contract = new Contract(web3Result, abi, contractAddress);
  74. await contract.initialize();
  75. return contract;
  76. }
  77. private async initialize(): Promise<void> {
  78. console.log("Contract initializing", this.contractAddress);
  79. // @ts-ignore
  80. this.contract = await new window.ethers.Contract(
  81. this.contractAddress,
  82. this.abi,
  83. this.web3Result.signer
  84. );
  85. }
  86. public async getContractBalance(){
  87. const balance = await this.contract.balanceOf(this.web3Result.account);
  88. console.log("balance", balance);
  89. let balanceNumber = await Web3Result.formatEther(balance);
  90. console.log("balanceNumber", balanceNumber);
  91. return balanceNumber;
  92. }
  93. private async isOwnerOf(tokenId: number): Promise<boolean> {
  94. console.log(
  95. "Checking ownership for token",
  96. tokenId,
  97. this.web3Result.account
  98. );
  99. const owner = await this.contract.ownerOf(tokenId);
  100. console.log("Ownership check result", owner, this.web3Result.account);
  101. return owner.toLowerCase() === this.web3Result.account.toLowerCase();
  102. }
  103. public async getAllTokensOfOwner(account: string): Promise<number[]> {
  104. try {
  105. // Get all Transfer events
  106. const filter = this.contract.filters.Transfer(null, account);
  107. const events = await this.contract.queryFilter(filter);
  108. // Also get transfers out to handle cases where tokens were transferred away
  109. const filterOut = this.contract.filters.Transfer(account, null);
  110. const eventsOut = await this.contract.queryFilter(filterOut);
  111. // Create a map to track current ownership
  112. const tokenMap = new Map<number, boolean>();
  113. // Process incoming transfers
  114. for (const event of events) {
  115. if (event.args) {
  116. const tokenId = event.args.tokenId.toNumber();
  117. tokenMap.set(tokenId, true);
  118. }
  119. }
  120. // Process outgoing transfers
  121. for (const event of eventsOut) {
  122. if (event.args) {
  123. const tokenId = event.args.tokenId.toNumber();
  124. tokenMap.delete(tokenId);
  125. }
  126. }
  127. // Convert map keys to array
  128. const tokenIds = Array.from(tokenMap.keys());
  129. console.log("Found tokens:", tokenIds);
  130. return tokenIds;
  131. } catch (error) {
  132. console.error("Get all tokens of owner failed:", error);
  133. return [];
  134. }
  135. }
  136. private async isApproved(
  137. tokenId: number,
  138. toAddress: string
  139. ): Promise<boolean> {
  140. console.log("Checking approval for token", tokenId, toAddress);
  141. const approvedAddress = await this.contract.getApproved(tokenId);
  142. console.log("Approval check result", approvedAddress, toAddress);
  143. return approvedAddress.toLowerCase() === toAddress.toLowerCase();
  144. }
  145. private async approve(tokenId: number, toAddress: string): Promise<boolean> {
  146. console.log("Approving token", tokenId, toAddress);
  147. try {
  148. const tx = await this.contract.approve(toAddress, tokenId);
  149. console.log("Approval transaction", tx);
  150. return true;
  151. } catch (error) {
  152. console.error("Approval failed:", error);
  153. return false;
  154. }
  155. }
  156. private async wallApproval(
  157. userAddress: string,
  158. receiveAddress: string,
  159. nftNo: number
  160. ) {
  161. console.log("Approval transaction", userAddress, receiveAddress, nftNo);
  162. const tx = this.contract.Approval(userAddress, receiveAddress, nftNo);
  163. console.log("Approval transaction", tx);
  164. return tx;
  165. }
  166. public async transferTo(
  167. userAddress: string,
  168. receiveAddress: string,
  169. nftNo: number
  170. ): Promise<ContractResult> {
  171. try {
  172. const fromAddress = userAddress.toLowerCase();
  173. const toAddress = receiveAddress.toLowerCase();
  174. // Then execute the transfer
  175. console.log("Executing transfer...");
  176. const transferTx = await this.contract.transferFrom(
  177. fromAddress,
  178. toAddress,
  179. nftNo
  180. );
  181. console.log("Transfer transaction hash:", transferTx.hash);
  182. await transferTx.wait();
  183. console.log("Transfer confirmed");
  184. return ContractResult.SUCCESS;
  185. } catch (error: any) {
  186. console.error("Transfer failed:", error);
  187. if (error.code === "INSUFFICIENT_FUNDS") {
  188. return ContractResult.GAS_ERROR;
  189. }
  190. return ContractResult.EXECUTION_ERROR;
  191. }
  192. }
  193. public async ownerAndApprove(
  194. tokenId: number,
  195. toAddress: string
  196. ): Promise<ContractResult> {
  197. console.log("Starting owner and approve check for token", tokenId);
  198. const isOwner = await this.isOwnerOf(tokenId);
  199. if (!isOwner) {
  200. return ContractResult.NOT_OWNER;
  201. }
  202. console.log("Ownership verified");
  203. const isApproved = await this.isApproved(tokenId, toAddress);
  204. if (!isApproved) {
  205. const approvalSuccess = await this.approve(tokenId, toAddress);
  206. if (!approvalSuccess) {
  207. return ContractResult.NOT_APPROVED;
  208. }
  209. }
  210. console.log("Approval verified");
  211. return ContractResult.SUCCESS;
  212. }
  213. public async burnSBTAndSoulNFT(
  214. sbtAddress: string,
  215. userId: number,
  216. sbtId: number,
  217. soulIds: number[]
  218. ): Promise<boolean> {
  219. try {
  220. const tx = await this.contract.fusionBurn(
  221. userId,
  222. sbtAddress,
  223. sbtId,
  224. soulIds
  225. );
  226. console.log("Burn SBT and Soul NFT transaction", tx);
  227. return true;
  228. } catch (error) {
  229. console.error("Burn SBT and Soul NFT failed:", error);
  230. return false;
  231. }
  232. }
  233. public async burnSBT(
  234. sbtAddress: string,
  235. userId: number,
  236. sbtId: number
  237. ): Promise<boolean> {
  238. try {
  239. const tx = await this.contract.burnNFT(sbtAddress, sbtId, userId);
  240. console.log("Burn SBT transaction", tx);
  241. return true;
  242. } catch (error) {
  243. console.error("Burn SBT failed:", error);
  244. return false;
  245. }
  246. }
  247. public async getBalanceOf(owner: string): Promise<number> {
  248. try {
  249. const balance = await this.contract.balanceOf(owner);
  250. return balance.toNumber();
  251. } catch (error) {
  252. console.error("Get balance failed:", error);
  253. return 0;
  254. }
  255. }
  256. public async getTokenOfOwnerByIndex(owner: string, index: number): Promise<number> {
  257. try {
  258. const tokenId = await this.contract.tokenOfOwnerByIndex(owner, index);
  259. return tokenId.toNumber();
  260. } catch (error) {
  261. console.error("Get token of owner by index failed:", error);
  262. return 0;
  263. }
  264. }
  265. public async getTokenURI(tokenId: number): Promise<string> {
  266. try {
  267. return await this.contract.tokenURI(tokenId);
  268. } catch (error) {
  269. console.error("Get token URI failed:", error);
  270. return "";
  271. }
  272. }
  273. }