Solidity 디자인 패턴에 대한 개발자 가이드

in #kr-devlast year

개발자는 다양한 온라인 리소스에서 Solidity를 사용하는 방법을 배울 수 있지만 이러한 자료는 Solidity에서 구현하는 방법과 스타일이 다양하기 때문에 동일하지 않습니다.

디자인 패턴은 재사용이 가능하며 반복되는 디자인 결함을 해결하는 데 사용되는 기존 솔루션입니다. 한 주소에서 다른 주소로 전송하는 것은 디자인 패턴으로 규제할 수 있는 Solidity의 빈번한 문제의 실제적인 예입니다.

Solidity에서 Ether를 전송할 때 , 또는 방법을 Send사용 Transfer합니다 Call. 이 세 가지 방법은 동일한 단일 목표를 가지고 있습니다. 스마트 계약에서 Ether를 보내는 것입니다. 이 목적을 위해 Transfer및 메서드 를 사용하는 방법을 살펴보겠습니다 . Call다음 코드 샘플은 다양한 구현을 보여줍니다.

먼저 Transfer방법입니다. 이 접근 방식을 사용할 때 수신하는 모든 스마트 계약은 폴백 기능을 정의해야 합니다. 그렇지 않으면 전송 트랜잭션이 실패합니다. 사용 가능한 가스 한도는 2300가스로, 이체 거래를 완료하기에 충분하며 재진입 공격을 방지하는 데 도움이 됩니다.

기능 이체 ( 수취인 주소 _to ) 공채 {      
  _to . 전송 ( msg . 값 ); }   
 

위의 코드 스니펫 Transfer은 수신 주소를 받아들이고 로 지정된 Ether의 전송을 시작하는 방법을 _to사용하는 함수를 정의합니다 ._to.transfer``msg.value

다음은 Call방법입니다. 계약의 다른 기능은 이 방법을 사용하여 트리거할 수 있으며 선택적으로 기능이 실행될 때 사용할 가스 요금을 설정할 수 있습니다.

함수 호출 ( 주소 지불 가능 _to ) 공공 지불 가능 { ( bool 보낸 ) = _to . 전화 _ 가스 ( 1000 ){ 값 : msg . 값 }( "" ); require ( "보냈지만 이더는 보내지 않았습니다" ); }  
     
    

위의 코드 스니펫 Call은 수신 주소를 로 받아들이고 _to트랜잭션 상태를 부울로 설정하고 반환된 결과를 데이터 변수에 제공하는 함수를 정의합니다. 가 비어 있으면 함수는 메서드 직후에 실행 됩니다. 수신 기능이 구현되지 않은 경우 폴백이 실행됩니다.msg.data``receive``Call

스마트 계약 간에 Ether를 전송하는 가장 선호되는 방법은 이 Call방법을 사용하는 것입니다.

위의 예에서 우리는 Ether를 전송하기 위해 두 가지 다른 기술을 사용했습니다. 를 사용하여 소비할 가스의 양을 지정할 수 Call있지만 Transfer기본적으로 고정된 양의 가스가 있습니다.

이러한 기술은 의 반복 발생을 구현하기 위해 Solidity에서 실행되는 패턴입니다 Transfer.

맥락을 유지하기 위해 다음 섹션은 Solidity가 규제한 디자인 패턴 중 일부입니다.

행동 패턴

가드 체크

스마트 계약의 주요 기능은 트랜잭션의 요구 사항을 통과시키는 것입니다. 조건이 실패하면 계약이 이전 상태로 되돌아갑니다. Solidity는 EVM의 오류 처리 메커니즘을 사용하여 예외를 발생시키고 계약을 예외 이전의 작동 상태로 복원함으로써 이를 달성합니다.

아래의 스마트 계약은 세 가지 기술을 모두 사용하여 가드 체크 패턴을 구현하는 방법을 보여줍니다.

계약 기여 { 함수 기여 ( 주소 _from ) 지불 가능 공개 { 요구 ( msg . 값 != 0 ); 요구 ( _from != 주소 ( 0 )); 
    단위 prevBalance = this . 균형 ; 
    단위 금액 ; 
   
      
     

    if ( _from . balance == 0 ) { 
      금액 = msg . 가치 ; } else if ( _from.balance < msg.sender.balance ) { 금액 = msg . _ _ _ 
      _ _ _ 값 / 2 ; } else { 
      revert ( "잔고 부족!!!" ); }  
          
      
    

    _ 에서 . 이체 ( 금액 ); assert ( this . balance == prevBalance - amount ); } }
    
  

위의 코드 스니펫에서 Solidity는 다음을 사용하여 오류 예외를 처리합니다.

require()함수가 실행되는 조건을 선언합니다. 단일 조건을 인수로 받아들이고 조건이 거짓으로 평가되면 예외를 발생시켜 가스를 태우지 않고 함수 실행을 종료합니다.

assert()함수의 조건을 평가한 다음 예외를 발생시키고 계약을 이전 상태로 되돌리고 실행 후 요구 사항이 실패하면 가스 공급을 소비합니다.

revert()예외를 던지고, 제공된 가스를 반환하고, 함수에 대한 요구 사항이 실패하면 함수 호출을 계약의 원래 상태로 되돌립니다. 이 메서드는 조건을 평가하거나 요구하지 않습니다.revert()

상태 머신

상태 시스템 패턴은 이전 및 현재 입력을 기반으로 시스템의 동작을 시뮬레이션합니다. 개발자는 이 접근 방식을 사용하여 큰 문제를 간단한 단계와 전환으로 분류한 다음 애플리케이션의 실행 흐름을 나타내고 제어하는 데 사용합니다.

상태 시스템 패턴은 아래 코드 스니펫과 같이 스마트 계약에서도 구현할 수 있습니다.

contract Safe { 단계 공개 단계 = 단계 . 예치금 수락 ; uint public creationTime = 지금 ; 
    매핑 ( 주소 => uint ) 잔액 ; 
      
      

    수정자 atStage ( 단계 _stage ) { 요구합니다 ( 단계 == _stage ); 
      _ ; } 
      
    

    modifier timedTransitions () { if ( stage == Stages . AcceptingDeposits && now >= 
      creationTime + 1 days ) 
      nextStage (); if ( stage == Stages . FreezingDeposits && now >= 
      creationTime + 4 days ) 
      nextStage (); 
      _ ; } 함수 nextStage () 내부 { 
          
          
    
      
      stage = Stages(uint(stage) + 1);
    }
    function deposit() public payable timedTransitions atStage(Stages.AcceptingDeposits) {
      balances[msg.sender] += msg.value;
    }
    function withdraw() public timedTransitions atStage(Stages.ReleasingDeposits) {
      uint금액 = 잔액 [ msg . 발신자 ]; 
      잔액 [ msg . 발신자 ] = 0 ; 
      msg . 발신자 . 이체 ( 금액 ); } }  
    

위의 코드 스니펫에서 Safe계약은 수정자를 사용하여 다양한 단계 사이의 계약 상태를 업데이트합니다. 단계에 따라 입금 및 인출이 가능한 시기가 결정됩니다. 계약의 현재 상태가 가 아니면 AcceptingDeposit사용자는 계약에 입금할 수 없으며, 현재 상태가 가 아니면 ReleasingDeposit사용자는 계약에서 탈퇴할 수 없습니다.

신탁

Ethereum 계약에는 통신하는 자체 생태계가 있습니다. 시스템은 트랜잭션을 통해서만 외부 데이터를 가져올 수 있습니다(메소드에 데이터를 전달하여). 이는 많은 계약 사용 사례가 블록체인 이외의 소스(예: 주식 시장)의 지식을 포함하기 때문에 단점입니다.


LogRocket의 더 많은 훌륭한 기사:


이 문제에 대한 한 가지 해결책은 외부 세계와 연결된 오라클 패턴을 사용하는 것입니다. 오라클 서비스와 스마트 컨트랙트가 비동기적으로 통신할 때 오라클 서비스는 API 역할을 합니다. 트랜잭션은 오라클에 요청을 보내는 명령으로 구성된 스마트 계약 기능을 호출하여 시작됩니다.

이러한 요청의 매개변수를 기반으로 오라클은 결과를 가져오고 기본 계약에서 콜백 함수를 실행하여 반환합니다. 오라클 기반 계약은 단일 조직 또는 그룹의 정직성에 의존하기 때문에 분산 네트워크의 블록체인 개념과 호환되지 않습니다.

Oracle 서비스 2122 는 제공된 데이터로 유효성 검사를 제공하여 이 결함을 해결합니다. 오라클은 콜백 호출에 대해 비용을 지불해야 합니다. 따라서 콜백 호출에 필요한 Ether와 함께 오라클 비용이 지불됩니다.

아래 코드 스니펫은 오라클 계약과 소비자 계약 간의 트랜잭션을 보여줍니다.

계약 API { 
    주소 trustAccount = 0x000 ...; //계정 주소 struct Request { 
        바이트 데이터 ; 함수 ( 바이트 메모리 ) 외부 콜백 ; } 요청 [] 요청 ; 이벤트 NewRequest ( 단위 );  
      
        
    
    
     

    한정자 onlyowner ( 주소 계정 ) { 요구 ( msg . 발신자 == 계정 ); 
        _ ; } 함수 쿼리 ( 바이트 데이터 , 함수 ( 바이트 메모리 ) 외부 콜백 ) 공개 { 
        요청 . push ( 요청 ( 데이터 , 콜백 )); NewRequest ( 요청 . 길이 - 1 ); 
        
    
       
         
    } // 외부 함수 응답 에 의해 호출됨 ( uint requestID , 바이트 응답 ) public 
    onlyowner ( trustedAccount ) { 
    requests [ requestID ]. 콜백 ( 응답 ); } }
    
      
    

위의 코드 스니펫에서 API스마트 계약은 함수 knownSource를 사용하여 쿼리 요청을 보냅니다. 함수는 query함수를 실행하고 external callback함수를 사용 reply하여 외부 소스에서 응답 데이터를 수집합니다.

임의성

Solidity에서 임의의 고유한 값을 생성하는 것이 얼마나 까다로운지에도 불구하고 수요가 많습니다. 블록 타임스탬프는 이더리움에서 무작위성의 원천이지만 광부가 이를 조작할 수 있기 때문에 위험합니다. 이 문제를 방지하기 위해 블록 해시 PRNG 및 Oracle RNG와 같은 솔루션이 만들어졌습니다.

다음 코드 스니펫은 가장 최근 블록 해시를 사용하여 이 패턴의 기본 구현을 보여줍니다.

// 이 메서드는 예측 가능합니다. 주의하여 사용하십시오! function random () 내부 뷰는 ( uint ) { return uint ( blockhash ( block . number - 1 )); }
  
      

위 의 함수는 블록 번호( 블록체인의 변수인 )를 해싱하여 임의의 고유한 정수를 생성합니다 .randomNum()``block.number

보안 패턴

액세스 제한

Solidity에는 실행 권한을 관리하는 기본 제공 수단이 없기 때문에 한 가지 일반적인 경향은 함수 실행을 제한하는 것입니다. 기능 실행은 타이밍, 호출자 또는 트랜잭션 정보 및 기타 기준과 같은 특정 조건에서만 수행되어야 합니다.

다음은 함수 조건 지정의 예입니다.

계약 RestrictPayment { uint public date_time = 지금 ; 
     

    수정자만 ( 주소 계정 ) { 필수 ( msg . 발신자 == 계정 ); 
        _ ; } 
        
    

    function f () payable onlyowner ( date_time + 1 minutes ){ //코드가 여기에 옵니다 } } 
      
    

위의 Restrict 계약 은 기능 실행과 account다른 것을 방지합니다 . 함수에 대한 요구 사항이 충족되지 않으면 함수가 실행되기 전에 예외를 throw하는 데 사용됩니다.msg.sender``payable``payable``require

효과 상호 작용 확인

확인 효과 상호 작용 패턴은 외부 호출 이후 제어 흐름을 인계하려는 악의적인 계약의 위험을 줄입니다. 계약은 Ether 전송 절차 중에 제어 흐름을 외부 엔터티로 이전할 가능성이 있습니다. 외부 계약이 악의적인 경우 제어 흐름을 방해하고 발신자가 바람직하지 않은 상태로 리바운드될 가능성이 있습니다.

이 패턴을 사용하려면 취약점의 가능한 원인을 찾으면 대응할 수 있도록 함수의 어떤 부분이 취약한지 알아야 합니다.

다음은 이 패턴을 사용하는 방법의 예입니다.

계약 CheckedTransactions { 
    매핑 ( 주소 => 단위 ) 잔액 ; function deposit () public payable { 
        balances [ msg . 발신자 ] = msg . 가치 ; }  
      
    

    기능 인출 ( uint amount ) public { require ( balances [ msg . sender ] >= amount ); 
        잔액 [ msg . 보내는 사람 ] -= 금액 ; 
        msg . 발신자 . 이체 ( 금액 ); } }  
          
    

위의 코드 조각에서 메서드는 조건 이 실패 할 경우 예외를 throw하는 데 사용됩니다 . 즉, 사용자 는 .require()``balances[msg.sender] >= amount``amount``msg.sender

보안 Ether 전송

암호화폐 전송은 Solidity의 주요 기능은 아니지만 자주 발생합니다. 앞서 논의한 바와 같이 Transfer, CallSend는 Ether를 Solidity로 전송하기 위한 세 가지 기본 기술입니다. 차이점을 알지 못하는 한 사용할 방법을 결정하는 것은 불가능합니다.

Transfer앞에서 설명한 두 가지 방법( 및 ) 외에도 이 방법 Call을 사용하여 Solidity에서 Ether를 전송할 수 있습니다 Send.

Send``Transfer기본(2300)과 동일한 양의 가스를 소비한다는 점에서 유사합니다 . 그러나 와 달리 성공 Transfer여부를 나타내는 부울 결과를 반환합니다 . Send대부분의 Solidity 프로젝트는 더 이상 이 Send방법을 사용하지 않습니다.

다음은 Send메서드 구현입니다.

함수 보내기 ( 주소 지불 가능 _to ) 외부 지불 가능 { bool sent = _to . 전송 ( 123 ); 요구 ( 전송 됨 , "전송 실패" ); }
    
     

위 의 함수 는 에서 반환된 값이 인 경우 예외를 throw하는 함수를 send사용합니다 .require()``Boolean``_to.send(123)``false

풀오버푸시

이 디자인 패턴은 Ether 전송의 위험을 계약에서 사용자로 이동시킵니다. Ether 전송 중에 몇 가지 문제가 발생하여 트랜잭션이 실패할 수 있습니다. pull-over-push 패턴에서는 전송을 시작하는 주체(계약 작성자), 스마트 계약 및 수신자의 세 당사자가 관련됩니다.

이 패턴에는 사용자의 미결제 잔액을 추적하는 데 도움이 되는 매핑이 포함됩니다. 계약에서 수령인에게 Ether를 전달하는 대신 사용자는 할당된 Ether를 인출하는 기능을 호출합니다. 전송 중 하나의 부정확성은 다른 트랜잭션에 영향을 미치지 않습니다.

다음은 풀 오버 풀의 예입니다.

계약 ProfitsWithdrawal { 
    매핑 ( 주소 => 단위 ) 이익 ; 함수 allowPull ( 주소 소유자 , 단위 금액 ) private { 
        이익 [ 소유자 ] += 금액 ; } functionwithdrawalProfits ( ) public { uint amount = profit [ msg . 발신자 ]; 요구하다 ( 금액 != 0  
        
    
      
        
         ); 요구 ( 주소 ( 이 ). 잔고 >= 금액 ); 
        이익 [ msg . 발신자 ] = 0 ; 
        msg . 발신자 . 이체 ( 금액 ); } }
          
    

위의 계약 에서 사용자의 잔액이 사용자에게 할당된 이익보다 크거나 같은 경우 사용자가 ProfitsWithdrawal매핑된 이익을 인출할 수 있습니다.address

비상 정지

감사된 스마트 계약에는 사이버 사고에 연루될 때까지 감지되지 않는 버그가 포함될 수 있습니다. 계약 실행 후 발견된 오류는 수정하기 어려울 것입니다. 이 디자인의 도움으로 중요한 기능에 대한 호출을 차단하여 스마트 계약이 수정될 때까지 공격자를 방지하여 계약을 중단할 수 있습니다.

승인된 사용자만 중지 기능을 사용하도록 허용하여 사용자가 남용하지 않도록 해야 합니다. 상태 변수는 계약 종료를 결정하기 위해 from falseto to로 설정됩니다. true계약 종료 후 액세스 제한 패턴을 사용하여 중요한 기능이 실행되지 않도록 할 수 있습니다.

상태 변수가 비상 정지 캔의 시작을 나타내는 경우 예외를 발생시키는 함수 수정은 아래와 같이 이를 수행하는 데 사용됩니다.

계약 EmergencyStop { bool Running = true ; 
    주소 trustAccount = 0x000 ...; //계정 주소 
    수정자 stillRunning { require ( Running ); 
        _ ; } 
    수정자 NotRunning { 요구 (¡ 실행 중 !); 
        _ ; } 
    modifier onlyAuthorized ( address account ) { require ( msg . sender == account 
         
        
     
        
     
        ); 
        _ ; } function stopContract () public onlyAuthorized ( trustedAccount ) { Running = false ; } function resumeContract () public onlyAuthorized ( trustedAccount ) { Running = true ; } }
    
      
          
    
      
          
    

위 의 EmergencyStop계약은 수정자를 사용하여 조건을 확인하고 이러한 조건 중 하나라도 충족되면 예외를 발생시킵니다. 계약은 긴급 상황을 처리하기 위해 및 기능을 사용합니다.stopContract()``resumeContract()

상태 변수를 로 재설정하여 계약을 재개할 수 있습니다 false. 이 방법은 비상 정지 기능과 같은 방식으로 무단 호출로부터 보호되어야 합니다.

업그레이드 가능성 패턴

대리 대리인

이 패턴을 사용하면 구성 요소를 손상시키지 않고 스마트 계약을 업그레이드할 수 있습니다. Delegatecall이 방법을 사용할 때 호출되는 특정 메시지 가 사용됩니다. 함수 서명을 노출하지 않고 대리자에게 함수 호출을 전달합니다.

프록시 계약의 대체 기능은 이를 사용하여 각 기능 호출에 대한 전달 메커니즘을 시작합니다. 반환 되는 유일한 것은 Delegatecall실행이 성공했는지 여부를 나타내는 부울 값입니다. 우리는 함수 호출의 반환 값에 더 관심이 있습니다. 컨트랙트를 업그레이드할 때 스토리지 순서를 변경해서는 안 됩니다. 추가만 허용됩니다.

다음은 이 패턴을 구현하는 예입니다.

계약 UpgradeProxy { 
    주소 위임 ; 
    주소 소유자 = msg . 발신자 ; function upgradeDelegate ( 주소 newDelegateAddress ) public { require ( msg . sender == owner ); 대리자 = newDelegateAddress ; } function () 외부 지급 { 
        assembly { let _target := sload ( 0 ) 
      
        
         
    
    
            
            calldatacopy ( 0x01 , 0x01 , calldatasize ) let result := delegatecall ( gas , _target , 0x01 , calldatasize , 0x01 , 0 ) 
            returndatacopy ( 0x01 , 0x01 , returndatasize ) 결과 케이스 0 전환 { revert ( 0 , 0 )} default { return ( 0 
                
                  , returndatasize )} } } }
        
    

위의 코드 조각 에서 계약 데이터의 복사본을 새 버전으로 전송하는 폴백 함수를 호출하여 계약을 실행 한 후 계약을 업그레이드 UpgradeProxy할 수 있는 메커니즘을 처리합니다 .delegate``owner``delegate

메모리 어레이 구축

이 방법은 빠르고 효율적으로 계약 저장소에서 데이터를 집계하고 검색합니다. 계약의 메모리와 상호 작용하는 것은 EVM에서 가장 비용이 많이 드는 작업 중 하나입니다. 중복을 제거하고 필요한 데이터만 저장하면 비용을 최소화할 수 있습니다.

보기 기능 수정을 사용하여 추가 비용을 발생시키지 않고 계약 저장소에서 데이터를 집계하고 읽을 수 있습니다. 어레이를 스토리지에 저장하는 대신 검색이 필요할 때마다 메모리에 다시 생성됩니다.

배열과 같이 쉽게 반복할 수 있는 데이터 구조는 데이터 검색을 더 쉽게 만드는 데 사용됩니다. 여러 속성이 있는 데이터를 처리할 때 struct와 같은 사용자 정의 데이터 유형을 사용하여 집계합니다.

각 집계 인스턴스에 대한 예상 데이터 입력 수를 추적하려면 매핑도 필요합니다.

아래 코드는 이 패턴을 보여줍니다.

계약 저장소 { 구조체 항목 { 문자열 이름 ; 
        uint32 가격 ; 
        주소 소유자 ; } 항목 [] 공용 항목 ; 
    매핑 ( 주소 => uint ) public itemsOwned ; function getItems ( address _owner ) public view 반환 ( uint [] memory ) { uint \[ ] memory result = new 
      
        
    
       
      
          uint [ \] ( itemsOwned [ _owner ]); 단위 카운터 = 0 ; for ( uint i = 0 ; i < items . length ; i ++) { if ( items [ i ]. owner == _owner ) { 
                result [ 카운터 ] = i ; 
                카운터 ++; } } 반환
         
           
               
            
        
        결과 ; } }
    

위의 Store계약에서 우리 struct는 목록에 있는 항목의 데이터 구조를 설계하는 데 사용하고 항목을 해당 소유자의 에 매핑했습니다 address. 주소가 소유한 항목을 가져오려면 getItems함수를 사용하여 이라는 메모리를 집계합니다 result.

영원한 저장

이 패턴은 업그레이드된 스마트 계약의 메모리를 유지합니다. 이전 계약과 새 계약이 블록체인에 별도로 배포되기 때문에 누적된 저장소는 사용자 정보, 계정 잔액 및 기타 중요한 정보에 대한 참조가 저장되는 이전 위치에 남아 있습니다.

영구 스토리지는 각 데이터 유형에 대해 하나씩 여러 데이터 스토리지 매핑을 구현하여 데이터 스토리지에 대한 수정을 방지하기 위해 가능한 한 독립적이어야 합니다. 추상화된 값을 sha3 해시 맵으로 변환하는 것이 키-값 저장소 역할을 합니다.

제안된 솔루션은 기존의 가치 저장보다 더 정교하기 때문에 래퍼는 복잡성을 줄이고 코드를 읽기 쉽게 만들 수 있습니다. 영구 스토리지를 사용하는 업그레이드 가능한 계약에서 래퍼를 사용하면 익숙하지 않은 구문과 해시가 있는 키를 더 쉽게 처리할 수 있습니다.

아래 코드 스니펫은 래퍼를 사용하여 영구 스토리지를 구현하는 방법을 보여줍니다.

function getBalance ( 주소 계정 ) 공용 보기 반환 ( uint ) { return eternalStorageAdr . getUint ( keccak256 ( "잔액" , 계정 )); } 기능 setBalance ( 주소 계정 , 단위 금액 ) 내부 { 
    EternalStorageAdr . setUint ( keccak256 ( "잔액" , 계정 ), 금액 ); }  
    

   

기능 addBalance ( 주소 계정 , 단위 금액 ) 내부 { 
    setBalance ( 계정 , getBalance ( 계정 ) + 금액 ); }    

위의 코드 스니펫에서 우리는 의 해시 함수를 사용 하여 계정의 잔액을 설정하기 위해 account영구 저장소에서 의 잔액을 얻었습니다.keccak256``enternalStorageAdr.getUint()

메모리 대 스토리지

Storage, memory, 또는 는 동적 데이터 타입의 위치를 변수 형태로 선언할 때 사용하는 방법이지만 지금은 and 에 calldata집중하겠습니다 . 이 용어 는 스마트 계약의 모든 인스턴스에서 공유되는 상태 변수를 의미하는 반면 각 스마트 계약 실행 인스턴스의 데이터에 대한 임시 저장 위치를 나타냅니다. 이것이 어떻게 작동하는지 알아보기 위해 아래 코드의 예를 살펴보겠습니다.memory``storage``storage``memory

다음 을 사용하는 예 storage:

계약 예산 계획 { struct Expense { uint price ; 문자열 항목 ; }  
        매핑 ( 주소 => 비용 ) 공공 비용 ; function purchase () external { 경비 보관 카트 = 경비 [ msg . 보내는 사람 ] 
                카트 . string = "딸기"  
                장바구니 . 가격 = 12 
          
                
                
           
        
                    
        } }

BudgetPlan계약에서 각 비용( Expense)이 price및 를 포함하는 구조체인 계정 비용에 대한 데이터 구조를 설계했습니다 item. purchase그런 다음 에 새 항목을 추가하는 함수를 선언 Expense했습니다 storage.

다음 을 사용하는 예 memory:

계약 예산 계획 { struct Expense { uint price ; 문자열 항목 ; }  
        매핑 ( 주소 => 비용 ) 공공 비용 ; function purchase () external { 비용 메모리 장바구니 = 비용 [ msg . 보내는 사람 ] 
                카트 . string = "딸기"  
                장바구니 . 가격 = 12 
          
                
                
           
        
                    
        } }

를 사용하는 예와 거의 비슷 storage하지만 모든 것이 동일하지만 코드 스니펫 에서 함수가 실행될 Expense때 메모리에 새 항목을 추가합니다.purchase

마무리 생각

특정 목표를 달성하거나 특정 개념을 구현하는 방법이 다르기 때문에 개발자는 디자인 패턴을 고수해야 합니다.

이러한 Solidity 디자인 패턴을 연습하면 응용 프로그램에 상당한 변화가 있음을 알 수 있습니다. 귀하의 애플리케이션은 기여하기 쉽고 깨끗하고 안전합니다.

다음 Solidity 프로젝트에서 이러한 패턴 중 하나 이상을 사용하여 이 주제에 대한 이해도를 테스트하는 것이 좋습니다.

이 주제와 관련된 질문을 하거나 아래 댓글 섹션에 의견을 남겨주십시오.

출처 : https://blog.logrocket.com/developers-guide-solidity-design-patterns/

Sort:  

[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.

Coin Marketplace

STEEM 0.28
TRX 0.12
JST 0.032
BTC 62205.45
ETH 2999.26
USDT 1.00
SBD 3.79