From eb0d9f8672fd796272067580ff1f3362352d5e57 Mon Sep 17 00:00:00 2001 From: Jesus Silva Date: Wed, 25 Oct 2023 06:42:04 -0400 Subject: [PATCH] adding ipfs upload, profile CRUD and others functions --- lib/pages/profile.dart | 152 +++++++++++++++++++++++++++++++++- lib/pages/upload_to_ipfs.dart | 33 ++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 lib/pages/upload_to_ipfs.dart diff --git a/lib/pages/profile.dart b/lib/pages/profile.dart index e676ccb..2c0c27f 100644 --- a/lib/pages/profile.dart +++ b/lib/pages/profile.dart @@ -1,11 +1,22 @@ +import 'dart:typed_data'; +import 'package:solana/dto.dart'; +import 'package:dinogrow/pages/upload_to_ipfs.dart'; import 'package:flutter/material.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:image_picker/image_picker.dart'; import 'package:go_router/go_router.dart'; import 'dart:async'; import 'dart:io'; - +import 'package:solana_common/utils/buffer.dart' as solana_buffer; +import 'package:solana_web3/solana_web3.dart' as web3; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:solana/solana.dart'; +import 'package:solana/solana.dart' as solana; import '../ui/widgets/widgets.dart'; +import 'package:solana/anchor.dart' as solana_anchor; +import 'package:solana/encoder.dart' as solana_encoder; +import '../anchor_types/put_profile_info.dart' as anchor_types_parameters; +import '../anchor_types/get_profile_info.dart' as anchor_types_parameters_get; class ProfileScreen extends StatefulWidget { const ProfileScreen({super.key}); @@ -231,6 +242,99 @@ class _ProfileScreenState extends State { setState(() { _loading = true; }); + var methodprogram = "saveprofile"; + final findprofileb = await findprofile(); + if (findprofileb != null) { + methodprogram = "updateprofile"; + print('Profile found'); + print(findprofileb.bio); + print(findprofileb.nickname); + print(findprofileb.status); + print(findprofileb.uri); + print(findprofileb.uri); + //https://quicknode.myfilebase.com/ipfs/+uri + } + final String? cid = await uploadToIPFS(imageProfile); + + //save profile + await dotenv.load(fileName: ".env"); + + SolanaClient? client; + client = SolanaClient( + rpcUrl: Uri.parse(dotenv.env['QUICKNODE_RPC_URL'].toString()), + websocketUrl: Uri.parse(dotenv.env['QUICKNODE_RPC_WSS'].toString()), + ); + const storage = FlutterSecureStorage(); + + final mainWalletKey = await storage.read(key: 'mnemonic'); + + final mainWalletSolana = await solana.Ed25519HDKeyPair.fromMnemonic( + mainWalletKey!, + ); + + //Generate internal wallet + String dinoString = dotenv.env['DINO_KEY'].toString(); + dinoString = dinoString.replaceAll(RegExp(r'\[|\]'), ''); + List valueStrings = dinoString.split(','); + List integerList = + (valueStrings).map((value) => int.parse(value)).toList(); + Uint8List secretKey = Uint8List.fromList(integerList); + final keypair = await web3.Keypair.fromSecretKey(secretKey); + //path: "m/44'/501'/0'/0'/0'", + String hdPath = dotenv.env['HD_PATH'].toString(); + final walletdino = await solana.Ed25519HDKeyPair.fromSeedWithHdPath( + seed: keypair.secretKey, hdPath: hdPath); + + final programId = dotenv.env['PROGRAM_ID'].toString(); + final systemProgramId = + solana.Ed25519HDPublicKey.fromBase58(solana.SystemProgram.programId); + + final programIdPublicKey = + solana.Ed25519HDPublicKey.fromBase58(programId); + + final dprofilePda = await solana.Ed25519HDPublicKey + .findProgramAddress(programId: programIdPublicKey, seeds: [ + solana_buffer.Buffer.fromString(dotenv.env['PROFILE_SEED'].toString()), + mainWalletSolana.publicKey.bytes, + ]); + + final instructions = [ + await solana_anchor.AnchorInstruction.forMethod( + programId: programIdPublicKey, + method: methodprogram, + arguments: solana_encoder.ByteArray( + anchor_types_parameters.PutProfileArguments( + nickname: nicknameController.text, + bio: bioController.text, + status: statusController.text, + uri: cid as String, + ).toBorsh().toList()), + accounts: [ + solana_encoder.AccountMeta.writeable( + pubKey: dprofilePda, isSigner: false), + solana_encoder.AccountMeta.writeable( + pubKey: mainWalletSolana.publicKey, isSigner: true), + solana_encoder.AccountMeta.readonly( + pubKey: systemProgramId, isSigner: false), + solana_encoder.AccountMeta.writeable( + pubKey: walletdino.publicKey, isSigner: true), + ], + namespace: 'global', + ), + ]; + final message = solana.Message(instructions: instructions); + final signature = await client.sendAndConfirmTransaction( + message: message, + signers: [mainWalletSolana, walletdino], + commitment: solana.Commitment.confirmed, + ); + + print('Tx successful with hash: $signature'); + + print(imageProfile.path); + print(nicknameController.text); + print(bioController.text); + print(statusController.text); // TODO update profile GoRouter.of(context).pop(); @@ -250,4 +354,50 @@ class _ProfileScreenState extends State { }); } } + + findprofile() async { + await dotenv.load(fileName: ".env"); + + SolanaClient? client; + client = SolanaClient( + rpcUrl: Uri.parse(dotenv.env['QUICKNODE_RPC_URL'].toString()), + websocketUrl: Uri.parse(dotenv.env['QUICKNODE_RPC_WSS'].toString()), + ); + const storage = FlutterSecureStorage(); + + final mainWalletKey = await storage.read(key: 'mnemonic'); + + final mainWalletSolana = await solana.Ed25519HDKeyPair.fromMnemonic( + mainWalletKey!, + ); + + final programId = dotenv.env['PROGRAM_ID'].toString(); + + final programIdPublicKey = solana.Ed25519HDPublicKey.fromBase58(programId); + + final dprofilePda = await solana.Ed25519HDPublicKey + .findProgramAddress(programId: programIdPublicKey, seeds: [ + solana_buffer.Buffer.fromString(dotenv.env['PROFILE_SEED'].toString()), + mainWalletSolana.publicKey.bytes, + ]); + + // Obtener todas las cuentas del programa + final accountprofile = await client.rpcClient + .getAccountInfo( + dprofilePda.toBase58(), + encoding: Encoding.base64, + ) + .value; + + if (accountprofile != null) { + final bytes = accountprofile.data as BinaryAccountData; + final decodeAllData = + anchor_types_parameters_get.GetProfileArguments.fromBorsh( + bytes.data as Uint8List); + return decodeAllData; + } else { + print('No profile found'); + return null; + } + } } diff --git a/lib/pages/upload_to_ipfs.dart b/lib/pages/upload_to_ipfs.dart new file mode 100644 index 0000000..5c397b2 --- /dev/null +++ b/lib/pages/upload_to_ipfs.dart @@ -0,0 +1,33 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; +import 'package:http/http.dart' as http; +import 'package:flutter_dotenv/flutter_dotenv.dart'; + +Future uploadToIPFS(File file) async { + await dotenv.load(fileName: ".env"); + final apiKeyQuicknodeIPFS = dotenv.env['QUICKNODE_API_URL_IPFS'].toString(); + const apiUrl = 'https://api.quicknode.com/ipfs/rest/v1/s3/put-object'; + String fileName = file.path.split('/').last; + final random = Random(); + final hash = List.generate(8, (index) => random.nextInt(10)).join(); + final hashedFileName = '${hash}_${fileName}'; + try { + print("inside the try catch"); + final headers = { + 'x-api-key': apiKeyQuicknodeIPFS, + }; + final request = http.MultipartRequest('POST', Uri.parse(apiUrl)) + ..headers.addAll(headers) + ..files.add(await http.MultipartFile.fromPath('Body', file.path)) + ..fields['Key'] = hashedFileName + ..fields['ContentType'] = 'text'; + final response = await http.Response.fromStream(await request.send()); + final jsonResponse = jsonDecode(response.body); + final cid = jsonResponse['pin']['cid']; + + return cid; + } catch (e) { + return null; + } +} \ No newline at end of file